Phương thức C # mà * có thể * là static có nên static không? [đóng cửa]


103

Các phương thức C # thể tĩnh có nên tĩnh không?

Hôm nay chúng ta đã thảo luận về vấn đề này và tôi đang ở trong hàng rào. Hãy tưởng tượng bạn có một phương thức dài mà bạn đã cấu trúc lại một vài dòng. Phương thức mới có thể nhận một vài biến cục bộ từ phương thức mẹ và trả về một giá trị. Điều này có nghĩa là nó có thể là tĩnh.

Câu hỏi đặt ra là: nên nó được tĩnh? Nó không tĩnh do thiết kế hoặc lựa chọn, đơn giản là bản chất của nó ở chỗ nó không tham chiếu đến bất kỳ giá trị phiên bản nào.


1
Bản sao khá chính xác của stackoverflow.com/questions/169378/…
JasonTrue

41
"khá chính xác" là một oxymoron
Matt Briggs 29/10/09

1
Resharper , công cụ Visual Studio của các công cụ, nói có! :-)
Rebecca

@Junto Có lẽ trong phiên bản của bạn, nhưng tôi nói có thể được thực hiện tĩnh ...
Robbie Dee

Câu trả lời:


59

Nó phụ thuộc. Thực sự có 2 loại phương thức tĩnh:

  1. Các phương thức tĩnh vì chúng CÓ THỂ
  2. Các phương thức tĩnh vì chúng PHẢI

Trong một cơ sở mã kích thước nhỏ đến trung bình, bạn thực sự có thể xử lý hai phương pháp này thay thế cho nhau.

Nếu bạn có một phương thức nằm trong danh mục đầu tiên (có thể là tĩnh) và bạn cần thay đổi nó để truy cập trạng thái lớp, thì tương đối phải tìm hiểu xem có thể biến phương thức tĩnh thành phương thức thể hiện hay không.

Tuy nhiên, trong một cơ sở mã lớn, số lượng lớn các trang web cuộc gọi có thể khiến việc tìm kiếm để xem liệu có thể chuyển đổi một phương thức tĩnh thành một phương thức không tĩnh quá tốn kém hay không. Nhiều lần mọi người sẽ thấy số lượng cuộc gọi và nói "ok ... Tốt hơn là tôi không nên thay đổi phương pháp này, mà thay vào đó hãy tạo một phương pháp mới thực hiện những gì tôi cần".

Điều đó có thể dẫn đến:

  1. Nhiều mã trùng lặp
  2. Sự bùng nổ về số lượng đối số phương thức

Cả hai điều đó đều tệ.

Vì vậy, lời khuyên của tôi là nếu bạn có cơ sở mã trên 200K LOC, tôi sẽ chỉ tạo các phương thức tĩnh nếu chúng là các phương thức phải tĩnh.

Việc tái cấu trúc từ non-static sang static tương đối dễ dàng (chỉ cần thêm một từ khóa), vì vậy nếu bạn muốn biến một can-be-static thành một static thực sự sau này (khi bạn cần chức năng của nó bên ngoài một phiên bản) thì bạn có thể. Tuy nhiên, việc tái cấu trúc ngược, biến một phương thức có thể là tĩnh thành một phương thức cá thể sẽ đắt hơn RẤT NHIỀU.

Với các cơ sở mã lớn, tốt hơn là bạn nên mắc lỗi ở khía cạnh dễ mở rộng, hơn là về độ thuần túy lý tưởng.

Vì vậy, đối với các dự án lớn, đừng làm cho mọi thứ trở nên tĩnh lặng, trừ khi bạn cần. Đối với các dự án nhỏ, chỉ cần làm những gì bạn thích nhất.


43

Tôi sẽ không làm cho nó trở thành thành viên tĩnh công khai của lớp đó . Lý do là việc đặt nó ở chế độ tĩnh công khai đang nói lên điều gì đó về kiểu của lớp: không chỉ rằng "kiểu này biết cách thực hiện hành vi này", mà còn "trách nhiệm của kiểu này phải thực hiện hành vi này." Và tỷ lệ cược là hành vi không còn bất kỳ mối quan hệ thực sự nào với loại lớn hơn.

Tuy nhiên, điều đó không có nghĩa là tôi sẽ không làm cho nó tĩnh. Hãy tự hỏi mình điều này: phương pháp mới có thể thuộc về nơi khác một cách hợp lý không? Nếu bạn có thể trả lời "có", có lẽ bạn muốn làm cho nó tĩnh (và di chuyển nó). Ngay cả khi điều đó không đúng, bạn vẫn có thể làm cho nó tĩnh. Chỉ cần không đánh dấu nó public.

Như một vấn đề thuận tiện, ít nhất bạn có thể đánh dấu nó internal. Điều này thường tránh cần phải di chuyển phương thức nếu bạn không dễ dàng truy cập vào một loại thích hợp hơn, nhưng vẫn để nó có thể truy cập khi cần thiết theo cách mà nó sẽ không hiển thị như một phần của giao diện công khai cho người dùng trong lớp của bạn .


6
Đã đồng ý. Tôi thường đặt phương thức "private static" trong trường hợp như thế này.
harpo

6
Tôi cũng sẽ không cho là tĩnh riêng tư. Điều chính là hãy tự hỏi bản thân: phương pháp này có phù hợp một cách hợp lý như một phần của loại hình này không, hay nó chỉ ở đây như một vấn đề thuận tiện? Nếu cái sau, nó có thể phù hợp hơn ở một nơi khác?
Joel Coehoorn

1
Giống như: "phương thức mới có thể thuộc về nơi khác một cách hợp lý" - manh mối là nó không dựa vào trạng thái cục bộ, có lẽ kiểu trả về là nơi nó thuộc về?
AndyM

20

Không cần thiết.

Chuyển các phương thức công khai từ tĩnh sang không tĩnh là một thay đổi đột phá và sẽ yêu cầu thay đổi đối với tất cả người gọi hoặc người tiêu dùng của bạn. Nếu một phương thức có vẻ giống như một phương thức cá thể, nhưng không sử dụng bất kỳ thành viên cá thể nào, tôi khuyên bạn nên đặt nó thành một phương thức cá thể như một biện pháp kiểm chứng trong tương lai.


Tôi nghĩ rằng anh ấy chủ yếu đề cập đến phương pháp tư nhân
George Mauer

@Ray - Đúng vậy, điều này chỉ áp dụng cho các thành viên không phải là riêng tư. Tôi đã cập nhật câu trả lời của mình để phản ánh điều này.
Michael

Suy nghĩ của tôi chính xác - chỉ vì bạn có thể làm một cái gì đó tĩnh không có nghĩa là bạn phải hoặc nên .....
marc_s

Vì vậy, bạn sẽ làm gì đối với các phương thức riêng tư có thể được đặt tĩnh?
Ray

@Ray - Có thể để nguyên như vậy cho đến khi tôi có đủ lý do chính đáng để chuyển sang trạng thái tĩnh.
Michael

13

Đúng. Lý do "nó có thể tĩnh" là nó không hoạt động trên trạng thái của đối tượng mà nó được gọi. Do đó, nó không phải là một phương thức thể hiện, mà là một phương thức lớp. Nếu nó có thể làm những gì nó cần làm mà không bao giờ truy cập dữ liệu cho ví dụ, thì nó phải là tĩnh.


11

Có, nó nên. Có nhiều số liệu khác nhau về việc ghép nối để đo lường cách lớp của bạn phụ thuộc vào những thứ khác, như các lớp khác, phương thức, v.v. Làm cho các phương thức tĩnh là một cách để giữ mức độ khớp nối giảm xuống, vì bạn có thể chắc chắn rằng một phương thức tĩnh không tham chiếu bất kỳ các thành viên.


7
Không cần thiết. Một phương thức tĩnh sẽ không truy cập thông tin cá thể, không có thành viên, như bạn đã nói, nhưng nó có thể tương tác với các lớp khác cũng như một phương thức phổ biến khác. Là tĩnh không có nghĩa là giảm khớp nối, nhất thiết. Tuy nhiên, tôi hiểu quan điểm của bạn.
Victor Rodrigues

Thay vào đó, static thực tế làm tăng khả năng ghép nối, bởi vì bạn bị giới hạn ở chính xác phương thức tĩnh đó, chẳng hạn như không có cách nào để iverride nó và do đó không có cách nào để thay đổi hành vi.
HimBromBeere,

8

Tôi nghĩ rằng nó sẽ làm cho nó dễ đọc hơn một chút nếu bạn đánh dấu nó là tĩnh ... Sau đó, ai đó đi cùng sẽ biết rằng nó không tham chiếu đến bất kỳ biến phiên bản nào mà không cần phải đọc toàn bộ hàm ...


6

Cá nhân tôi, tôi là một fan hâm mộ lớn của tình trạng vô quốc tịch. Phương thức của bạn có cần quyền truy cập vào trạng thái của lớp không? Nếu câu trả lời là không (và có thể là không, nếu không thì bạn sẽ không coi nó là một phương thức tĩnh), thì vâng, hãy tiếp tục.

Không có quyền truy cập vào trạng thái là bớt đau đầu. Cũng giống như việc ẩn các thành viên riêng tư không cần thiết bởi các lớp khác, bạn nên ẩn trạng thái khỏi các thành viên không cần nó. Truy cập giảm có thể có nghĩa là ít lỗi hơn. Ngoài ra, nó làm cho việc phân luồng dễ dàng hơn vì dễ dàng hơn nhiều để giữ các thành viên tĩnh an toàn cho luồng. Ngoài ra còn có một xem xét hiệu suất như thời gian chạy không cần phải vượt qua một tham chiếu đến này như một tham số cho các phương thức tĩnh.

Tất nhiên nhược điểm là nếu bạn thấy rằng phương thức tĩnh trước đây của bạn sẽ phải truy cập trạng thái vì một lý do nào đó, thì bạn phải thay đổi nó. Bây giờ tôi hiểu rằng đây có thể là một vấn đề đối với các API công khai, vì vậy nếu đây là một phương thức công khai trong một lớp công khai, thì có lẽ bạn nên suy nghĩ về hàm ý của điều này một chút. Tuy nhiên, tôi chưa bao giờ đối mặt với một tình huống trong thế giới thực nơi điều này thực sự gây ra vấn đề, nhưng có lẽ tôi chỉ may mắn.

Vì vậy, hãy làm cho nó, bằng mọi cách.


1
Một phương thức tĩnh vẫn có thể có quyền truy cập vào trạng thái. Nó chỉ nhận trạng thái từ đối tượng được truyền cho nó. Ngược lại, các trường cá thể không nhất thiết phải chứa trạng thái có thể thay đổi.
Jørgen Fogh

6

Các phương thức tĩnh nhanh hơn các phương thức không tĩnh nên có, chúng phải là tĩnh nếu có thể và không có lý do đặc biệt nào để để chúng ở chế độ không tĩnh .


3
Điều đó đúng về mặt kỹ thuật, nhưng không đúng về mặt vật chất. Sự khác biệt giữa tĩnh / không sẽ rất hiếm khi là một yếu tố trong hiệu suất.
Foredecker

22
-1: Sách giáo khoa tối ưu hóa sớm. Tốc độ chắc chắn không phải là tiêu chí đầu tiên khi quyết định tĩnh so với không tĩnh đối với trường hợp chung.
Không chắc

2
Đồng ý với Không chắc, đây sẽ là lý do cuối cùng để bạn xem xét tĩnh so với không.
Samuel

5
Quan điểm của tôi là tốc độ là một trong những lý do kém nhất mà bạn có thể có để quyết định xem một hàm là tĩnh hay không tĩnh trong 99% trường hợp. Có nhiều cân nhắc quan trọng hơn khi nói đến thiết kế OO mà các bài viết khác đã giải thích.
Không chắc

2
@agnieszka: Lý do đặc biệt của việc thường sử dụng phương thức instance thay vì phương thức tĩnh là trạng thái. Nếu bạn để các phương thức tĩnh, trong một ứng dụng phức tạp, bạn sẽ đưa ra các lỗi khiến bạn phải vò đầu bứt tóc. Hãy suy nghĩ về cách thay đổi trạng thái toàn cầu, điều kiện chủng tộc, thread-an toàn, vv
Sandor Davidhazi

5

Tôi ngạc nhiên rằng thực tế rất ít người đề cập đến tính năng đóng gói ở đây. Một phương thức instance sẽ tự động có quyền truy cập vào tất cả các trường, thuộc tính và phương thức private (instance). Ngoài tất cả những cái được bảo vệ kế thừa từ các lớp cơ sở.

Khi bạn viết mã, bạn nên viết nó sao cho bạn tiếp xúc ít nhất có thể và cũng để bạn có quyền truy cập ít nhất có thể.

Vì vậy, có thể điều quan trọng là làm cho mã của bạn nhanh, điều này sẽ xảy ra nếu bạn làm cho các phương thức của mình tĩnh, nhưng thường quan trọng hơn là làm cho mã của bạn không có khả năng tạo ra lỗi càng tốt. Một cách để đạt được điều đó là mã của bạn có quyền truy cập vào "nội dung riêng tư" càng ít càng tốt.

Thoạt nhìn, điều này có vẻ không liên quan vì OP rõ ràng đang nói về việc tái cấu trúc, điều này không thể sai trong trường hợp này và tạo ra bất kỳ lỗi mới nào, tuy nhiên mã được tái cấu trúc này phải được duy trì trong tương lai và được sửa đổi để làm cho mã của bạn bị "tấn công" lớn hơn bề mặt "liên quan đến các lỗi mới nếu nó có quyền truy cập vào các thành viên cá thể riêng tư. Vì vậy, nói chung, tôi nghĩ kết luận ở đây là "vâng, hầu hết các phương thức của bạn phải là tĩnh" trừ khi có bất kỳ lý do nào khác khiến chúng không tĩnh. Và điều này đơn giản bởi vì nó "sử dụng tốt hơn tính năng đóng gói và ẩn dữ liệu và tạo mã 'an toàn hơn'" ...


4

Tạo một cái gì đó tĩnh chỉ vì bạn có thể không phải là một ý kiến ​​hay. Các phương thức tĩnh nên tĩnh do thiết kế của chúng, không phải do ngẫu nhiên.

Giống như Michael đã nói, thay đổi điều này sau đó sẽ phá vỡ mã đang sử dụng nó.

Với điều đó đã nói, có vẻ như bạn đang tạo một hàm tiện ích riêng cho lớp mà trên thực tế là tĩnh theo thiết kế.


4

Nếu bạn có thể cấu trúc lại một vài dòng và phương thức kết quả có thể là tĩnh, đó có thể là dấu hiệu cho thấy các dòng bạn đã rút ra khỏi phương thức đó hoàn toàn không thuộc về lớp chứa và bạn nên cân nhắc chuyển chúng vào đẳng cấp của chính họ.


2

Cá nhân tôi sẽ không có lựa chọn nào khác ngoài việc làm cho nó tĩnh. Resharper đưa ra cảnh báo trong trường hợp này và PM của chúng tôi có quy tắc "Không có cảnh báo nào từ Resharper".


1
Bạn có thể thay đổi cài đặt ReSharper của mình: P
Bernhard Hofmann

1
Nếu nó dễ dàng như vậy, chúng tôi đã không có lời từ nào từ Resharper ... bao giờ :)
Prankster

1

Nó phụ thuộc nhưng nói chung tôi không làm cho các phương thức đó tĩnh. Mã luôn thay đổi và có lẽ một ngày nào đó tôi sẽ muốn biến hàm đó thành ảo và ghi đè nó trong một lớp con. Hoặc có lẽ một ngày nào đó nó sẽ cần tham chiếu đến các biến phiên bản. Sẽ khó thực hiện những thay đổi đó hơn nếu mọi địa chỉ cuộc gọi phải được thay đổi.


Đúng vậy, nhưng nếu một ngày nào đó bạn cần làm điều này, bạn luôn có thể làm cho nó không tĩnh. Hay tôi đang thiếu cái gì đó?
Ray

1
@Ray - Tôi đề cập đến vấn đề này trong câu trả lời của mình. static to non-static là một thay đổi đột phá, vì vậy tất cả những gì người tiêu dùng cần phải được sửa đổi. Không phải là một vấn đề lớn đối với một chương trình nhỏ, nhưng nếu nó là một thư viện cho người khác sử dụng thì điều này phải được tính đến.
Michael

Tất cả các trang web cuộc gọi phải được thay đổi từ một cuộc gọi phương thức tĩnh thành một cuộc gọi hàm thành viên.
Brian Ensink

Xin lỗi, bạn đã thêm điều đó sau bình luận của tôi. Trong đầu tôi đang nghĩ về các phương thức private, nhưng đó là một điểm hợp lệ cho các phương thức public.
Ray

... hoặc một ngày nào đó bạn có thể vứt bỏ nó. những đối số này không đủ tốt để tôi tạo ra một phương pháp không ổn định.
agnieszka

1

Tôi đề nghị rằng cách tốt nhất để suy nghĩ về nó là: Nếu bạn cần một phương thức lớp cần được gọi khi không có trường hợp nào của lớp được khởi tạo hoặc duy trì một số loại trạng thái toàn cục, thì tĩnh là một ý tưởng hay. Nhưng nói chung, tôi khuyên bạn nên tạo thành viên không tĩnh hơn.


1

Bạn nên nghĩ về các phương thức và lớp của mình:

  • Bạn sẽ sử dụng chúng như thế nào?
  • Bạn có cần nhiều gia nhập cho họ từ các cấp mã khác nhau không?
  • Đây có phải là một phương thức / lớp tôi có thể sử dụng trong hầu hết mọi dự án đáng suy nghĩ.

Nếu hai phần cuối cùng là 'có', thì phương thức / lớp của bạn có thể phải là tĩnh.

Ví dụ được sử dụng nhiều nhất có lẽ là Math lớp. Mọi ngôn ngữ OO chính đều có nó và tất cả các phương thức là tĩnh. Bởi vì bạn cần có thể sử dụng chúng mọi lúc, mọi nơi mà không cần tạo phiên bản.

Một ví dụ điển hình khác là Reverse()phương thức trong C #.
Đây là một phương thức tĩnh trongArray lớp. Nó đảo ngược thứ tự mảng của bạn.

Mã:

public static void Reverse(Array array)

Nó thậm chí không trả về bất cứ thứ gì, mảng của bạn bị đảo ngược, bởi vì tất cả các mảng đều là thể hiện của lớp Mảng.


Toán học là một ví dụ tồi. Tôi đoán nó tốt hơn: newnumber = number.round (2) so với newnumber = Math.round (number)
graffic

3
Lớp Toán là ví dụ hoàn hảo về việc sử dụng các lớp tĩnh. Đó là những gì chủ đề này nói về, không phải về cách bạn thích làm tròn số ...
KdgDev

1

Miễn là bạn đặt phương thức mới ở chế độ tĩnh, nó không phải là một thay đổi vi phạm. Trên thực tế, FxCop bao gồm hướng dẫn này như một trong những quy tắc của nó ( http://msdn.microsoft.com/en-us/library/ms245046(VS.80).aspx ), với thông tin sau:

Sau khi bạn đánh dấu các phương thức là tĩnh, trình biên dịch sẽ phát ra các trang web gọi không ảo cho các thành viên này. Việc phát các trang web cuộc gọi không ảo sẽ ngăn chặn việc kiểm tra trong thời gian chạy cho mỗi cuộc gọi để đảm bảo rằng con trỏ đối tượng hiện tại không phải là null. Điều này có thể dẫn đến mức tăng hiệu suất có thể đo lường được đối với mã nhạy cảm với hiệu suất. Trong một số trường hợp, việc không thể truy cập cá thể đối tượng hiện tại thể hiện vấn đề về tính đúng đắn.

Điều đó đang được nói, nhận xét đầu tiên từ David Kean tóm tắt ngắn gọn hơn những mối quan tâm bằng cách nói rằng điều này thực sự là chính xác hơn là về hiệu suất đạt được:

Mặc dù quy tắc này được phân loại là một vấn đề về hiệu suất, việc cải thiện hiệu suất của việc tạo một phương thức tĩnh chỉ khoảng 1%. Thay vào đó, nó là một vấn đề về tính đúng đắn có thể chỉ ra một thành viên chưa hoàn thành hoặc một lỗi do không sử dụng được các thành viên cá thể khác. Đánh dấu một phương thức là tĩnh ( Được chia sẻ trong Visual Basic) làm cho nó rõ ràng về ý định không chạm vào trạng thái cá thể.


1

Tôi chắc chắn sẽ biến mọi thứ tôi có thể thành tĩnh vì một lý do khác:

Các hàm tĩnh, khi JIT'd, được gọi mà không có tham số "this". Điều đó có nghĩa là, ví dụ, một hàm không tĩnh 3 tham số (phương thức thành viên) được đẩy với 4 tham số trên ngăn xếp.

Hàm tương tự được biên dịch như một hàm tĩnh sẽ được gọi với 3 tham số. Điều này có thể giải phóng các đăng ký cho JIT và bảo tồn không gian ngăn xếp ...


1

Tôi đang ở trại "chỉ tạo phương pháp riêng tư tĩnh". Tạo một phương thức công khai có thể giới thiệu kết hợp mà bạn không muốn và có thể làm giảm khả năng kiểm tra: Bạn không thể khai báo một phương thức tĩnh công khai.

Nếu bạn muốn kiểm tra đơn vị một phương pháp sử dụng phương thức tĩnh công khai, bạn cũng phải kiểm tra phương thức tĩnh, phương pháp này có thể không phải là điều bạn muốn.


1

Vốn dĩ các phương thức tĩnh vì một lý do nào đó được tạo thành không tĩnh chỉ đơn giản là gây phiền nhiễu. Nói một cách dí dỏm:

Tôi gọi cho ngân hàng của mình và yêu cầu số dư của tôi.
Họ yêu cầu số tài khoản của tôi.
Đủ công bằng. Phương pháp phiên bản.

Tôi gọi cho ngân hàng của mình và hỏi địa chỉ gửi thư của họ.
Họ yêu cầu số tài khoản của tôi.
WTF? Fail — lẽ ra phải là phương thức tĩnh.


1
Điều gì sẽ xảy ra nếu các khách hàng khác nhau được phục vụ bởi các chi nhánh khác nhau? Cuối cùng, thư từ gửi đến văn phòng chính của ngân hàng có thể được gửi đến chi nhánh thích hợp, nhưng điều đó không có nghĩa là khách hàng sẽ không được phục vụ tốt hơn bằng cách sử dụng địa chỉ của chi nhánh cụ thể phục vụ anh ta.
supercat

0

Tôi nhìn nó tổng quát từ góc độ chức năng của các chức năng thuần túy. Nó có cần phải là một phương thức thể hiện không? Nếu không, bạn có thể có lợi khi buộc người dùng chuyển các biến vào và không làm xáo trộn trạng thái của phiên bản hiện tại. (Chà, bạn vẫn có thể sử dụng trạng thái, nhưng vấn đề là, theo thiết kế, không nên làm như vậy.) Tôi thường thiết kế các phương thức thể hiện như là thành viên công khai và cố gắng hết sức để làm cho các thành viên riêng tư tĩnh. Nếu cần thiết (sau đó bạn có thể dễ dàng giải nén chúng vào các lớp khác sau này.


-1

Trong những trường hợp đó, tôi có xu hướng di chuyển phương thức sang thư viện tĩnh hoặc utils, vì vậy tôi không trộn khái niệm "đối tượng" với khái niệm "lớp"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.