C#中那些*可以*被静态化的方法应该被静态化吗?

113

可否将能够被定义为静态方法的C#方法设置为静态?

我们今天讨论了这个问题,我对此有些犹豫。假设您有一个长方法,并从中重构出几行代码。新方法可能会从父方法中获取一些本地变量并返回一个值。这意味着该方法可以被定义为静态方法。

问题是:它应该被定义为静态方法吗?它不是由设计或选择而静态化,只是因为其本质上没有引用任何实例值。


1
相当准确的副本 https://dev59.com/LXVC5IYBdhLWcg3w1E7w#169423 - JasonTrue
44
"fairly exact"是一个矛盾修饰语。 - Matt Briggs
1
Resharper,Visual Studio 的工具中的工具,说是的! :-) - Rebecca
@Junto 或许在你的版本中不行,但我的版本说可以将其设为静态... - Robbie Dee
21个回答

65
这要看情况而定。 实际上,静态方法可以分为两种类型:
1. 可以是静态的方法 2. 必须是静态的方法
在小到中等规模的代码库中,这两种方法可以互换使用。
如果您有一个属于第一类的方法(可以是静态的),并且您需要更改它以访问类状态,则相对容易确定是否可能将该静态方法转换为实例方法。
然而,在大型代码库中,大量的调用站点可能使得搜索是否可能将静态方法转换为非静态方法成本过高。很多时候人们会看到调用数量,然后说“好吧...我最好不要更改这个方法,而是创建一个新的方法来完成我所需的工作”。
这可能会导致以下问题之一:
1. 大量的代码重复 2. 方法参数数量激增
这两件事都不好。
因此,我的建议是,如果您的代码库超过200K LOC,则只应将必须为静态的方法设置为静态方法。
从非静态到静态的重构相对容易(只需添加一个关键字),因此,如果您想在需要其功能的情况下将可为静态的方法变成实际静态方法,那么可以随时这样做。但是,反向重构,将可为静态的方法转换为实例方法则成本要高得多。
对于大型代码库,最好在易于扩展的一面犯错误,而不是在纯理念的一面。
因此,对于大型项目,请只将必需为静态的方法设置为静态。对于小型项目,则可以随意处理。

46

我不会将其设为公共的静态成员变量。原因是将其设为公共静态的同时也在表达该类类型的某种含义:不仅是“这个类型知道如何执行这个行为”,还有“执行该行为是该类型的职责”。而大多数情况下,该行为与整个类型已经没有任何实质上的关联了。

当然,这并不意味着我不会将其设为静态的。你可以自问一下:新方法是否逻辑上应该属于其他类?如果你的答案是“是”,那么你可能希望将其设为静态(并移动它)。即使答案是否定的,它仍然可以被设为静态。只是不要标记为 public

出于方便起见,您至少可以将其标记为 internal 。这通常避免了需要移动该方法的情况,如果您没有容易访问到更合适的类型,但仍然以一种方式使其可访问,在需要的地方使用,而不会成为该类用户公共接口的一部分。


6
同意。在这种情况下,我通常会将该方法设置为"private static"。 - harpo
6
我不会假设是私有静态的,最重要的是问自己:这个方法在逻辑上是否适合作为这个类型的一部分,还是只是出于方便而在这里?如果是后者,它可能更适合放在其他地方吗? - Joel Coehoorn
2
“新方法在逻辑上可能属于其他地方” - 线索是它不依赖于本地状态,也许返回类型是它所属的地方? - AndyM

21

不一定。

将公共方法从静态变为非静态是一种破坏性的更改,需要对所有调用者或消费者进行更改。如果一个方法看起来像是实例方法,但恰好没有使用任何实例成员,我建议将其作为一种未来保护的措施,更改为实例方法。


我认为他主要是指私有方法。 - George Mauer
@Ray - 对,这只适用于非私有成员。我更新了我的答案以反映这一点。 - Michael
我的想法完全一样-仅仅因为你“可以”将某些东西设为静态的并不意味着你必须或应该这样做... - marc_s
那么对于可以被定义为静态方法的私有方法,你会怎么做呢? - Ray
@Ray - 可能要保持原样,直到我有足够好的理由转移到静态。 - Michael

13

是的。 "它可以是静态的"的原因是它不操作调用它的对象的状态。 因此,它不是实例方法,而是类方法。 如果它可以在不访问实例数据的情况下完成它需要做的事情,那么它应该是静态的。


11

是的,应该这样做。有各种耦合度度量指标来衡量你的类对其他内容的依赖程度,例如其他类、方法等。将方法改为静态方法可以降低耦合度,因为你可以确定静态方法不会引用任何成员变量。


8
不一定。静态方法不会访问实例信息和成员,就像你说的那样,但它可以与其他类以及另一个普通方法进行交互。静态并不意味着耦合一定减少。然而,我理解了你的观点。 - Victor Rodrigues
1
相反,静态方法实际上会增加耦合,因为您仅限于使用该静态方法,例如无法覆盖它,因此也无法更改其行为。 - MakePeaceGreatAgain

8

我认为如果您将其标记为静态,那么它会更易读……这样,来看的人就会知道它没有引用任何实例变量,而不必阅读整个函数……


6

静态方法比非静态方法更快,所以如果可以的话,它们应该是静态的,并且没有特殊的原因让它们保持非静态。


3
从技术上讲,这是正确的,但在实际物质意义上并不是。静态/非静态之间的区别在性能方面极其罕见。 - Foredecker
23
教科书式的过早优化。在一般情况下,当决定使用静态方法还是非静态方法时,速度肯定不应该是第一个标准。 - Not Sure
2
同意“不确定”所说的,这应该是您考虑静态与非静态的最后理由。 - Samuel
5
我的观点是,在99%的情况下,速度是决定函数是静态还是非静态的最差理由之一。在面向对象设计方面,有更为重要的考虑因素,其他帖子已经详细阐述了这些因素。 - Not Sure
2
@agnieszka:通常使用实例方法而不是静态方法的特殊原因是状态。如果您将方法保持为静态,在复杂的应用程序中,您将引入会让您抓狂的错误。请考虑修改全局状态、竞争条件、线程安全等问题。 - Sandor Davidhazi
显示剩余4条评论

6

个人而言,我非常喜欢无状态。您的方法需要访问类的状态吗?如果答案是否定的(很可能是否,否则您不会考虑将其作为静态方法),那么可以采用。

没有访问状态会减少麻烦。就像隐藏其他类不需要的私有成员一样,隐藏不需要状态的成员也是一个好主意。减少访问可以意味着减少错误。此外,它使线程更容易,因为保持静态成员的线程安全性要容易得多。还有一个性能考虑,因为运行时不需要传递对this的引用作为静态方法的参数。

当然,缺点是如果您发现以前的静态方法出于某种原因必须访问状态,那么您必须更改它。现在我明白了,这可能是公共API的问题,因此,如果这是公共类中的公共方法,则可能应该考虑一下其影响。尽管如此,在现实世界中,我从未遇到过这种情况真正造成问题,但也许我只是幸运。

所以,尽管放心使用吧。


2
一个静态方法仍然可以访问状态。它只是从传递给它的对象获取状态。相反,实例字段不一定包含可变状态。 - Jørgen Fogh
静态方法可以拥有自己的静态类字段,因此具有状态。这就像你所说的,只有在保持这样的协议时,才不会使其具有状态。 - FantomX1

5
我很惊讶这里很少提到封装。实例方法将自动访问所有私有(实例)字段、属性和方法,以及从基类继承的所有受保护字段、属性和方法。
编写代码时,应该尽可能地减少暴露,同时也尽可能地减少访问范围。
因此,是的,使代码快速可能很重要,这将发生在您使方法静态的情况下,但通常比这更重要的是使代码尽可能无法创建错误。其中一种方法是尽可能少地访问“私有内容”。
乍一看,这似乎与OP显然谈论的重构无关,因为在这种情况下不会出错并且不会创建任何新错误,但是未来必须维护和修改重构后的代码,如果它可以访问私有实例成员,则使您的代码具有更大的“攻击面”以便制造新错误。因此,总的来说,我认为结论是,“大多数情况下应该使您的方法静态”,除非有其他原因不使它们静态。这只是因为这是“更好的封装和数据隐藏使用,可以创建更安全的代码”。

4

仅仅因为你可以将某个东西变成静态的并不是一个好主意。静态方法应该是由设计而非偶然性而成为静态的。

就像Michael所说的那样,稍后更改会破坏使用它的代码。

话虽如此,听起来你正在为类创建一个私有实用函数,这个函数根据设计实际上是静态的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接