方法可以设为静态,但是应该吗?


366

Resharper喜欢在asp.net页面上指出可以静态化的多个功能。如果我将它们设为静态,对我有帮助吗?我应该将它们设为静态并将其移至实用程序类吗?


20
Resharper实际上不是在喊“低凝聚力,低凝聚力”吗?现在该看看该方法是否真正属于该类了。
PK 2010年

Answers:


245

静态方法与实例方法
10.2.5 C#语言规范的静态和实例成员解释了两者之间的区别。通常,静态方法与实例方法相比只能提供很小的性能增强,但是仅在某些极端情况下(有关此问题的更多详细信息,请参见此答案)。

FxCop或代码分析中的规则CA1822指出:

“在[将成员标记为静态]之后,编译器将向这些成员发出非虚拟调用站点,这将阻止在运行时检查每次调用,以确保当前对象指针为非空。这可以带来可观的性能提升对于性能敏感的代码。在某些情况下,无法访问当前对象实例表示正确性问题。”

实用
程序类除非在您的设计中有意义,否则不要将它们移至实用程序类。如果静态方法与特定类型相关,就像ToRadians(double degrees)方法与表示角度的类相关,则将该方法作为该类型的静态成员存在是有意义的(请注意,出于演示目的,这是一个复杂的示例)。


2
>编译器将向这些成员发出非虚拟调用站点。实际上就是“编译器可能会发出...”。我记得有关使用callvirt而不是通过调用来解决某些潜在错误的C#编译器的知识。
乔纳森·艾伦

24
我直接从FxCop 1.36剪切并粘贴。如果FxCop错误,则足够公平。
杰夫·耶茨

5
@Maxim不确定我是否喜欢“ bullshit”声明;对待陌生人的相当不礼貌的方式。但是,基础点是有效的。我已经进行了一些更新(这是9年前,所以我不记得我最初提出的主张的基础)。
杰夫·耶茨

10
@Maxim您的观点无效。我向你保证,我9年前的评价不高。我赞赏指出错误(或纠正错误的修改)的评论,但不要粗鲁,也不要对别人提出不合理的期望。不要称呼“废话”;这意味着对善良的错误或无知的意图是欺骗而不是诚实。这很粗鲁。我自愿花时间在这里提供帮助,如果对它不尊重,那真的感觉毫无意义。不要告诉我该冒犯什么-那是我的选择,而不是你的选择。了解如何尊重和正直地表达自己的观点。谢谢。
杰夫·耶茨

2
@Maxim虽然委托不是与每个实例一起存储的,但无状态类的每个实例的确确实在堆上占用了一些内存,这是无用的开销。通常,实例化服务不是应用程序中的热门路径,但是,如果您的应用程序最终构造了许多这样的对象,则会增加GC压力,而这可以通过简单地使用静态方法来避免。OP的原始主张是,在极端情况下,静态方法相对于无状态实例具有性能上的好处,它是适当地细化和有效的。
Asad Saeeduddin

259

在我看来,性能,名称空间污染等都是次要的。问问自己什么是合乎逻辑的。该方法是在逻辑上对类型的实例进行操作,还是与类型本身相关?如果是后者,则使其成为静态方法。如果它与不受您控制的类型相关,则仅将其移至实用程序类。

有时也有其逻辑上作用于一个实例,但不要发生在使用任何实例的状态的方法还没有。例如,如果您正在构建文件系统,并且已经有了目录的概念,但是尚未实现它,则可以编写一个返回文件系统对象类型的属性,并且该属性总是“文件”-但在逻辑上与实例相关,因此应该是一个实例方法。如果要使方法虚拟化,这也很重要-您的特定实现可能不需要状态,但是派生类可能需要状态。(例如,询问集合是否为只读-您可能尚未实现该集合的只读形式,但这显然是集合本身的属性,而不是类型。)


1
我认为优秀的短毛猫应该可以选择将消息限制为非虚拟方法,因为基类方法实际上什么都不做很常见。覆盖方法通常会执行某些操作,但并非总是如此。有时,为空的iEnumerable之类的类提供类很有用,该类的方法本质上会忽略该实例,但是需要该实例来选择要使用的正确方法。
超级猫

2
“有时有些方法在逻辑上对一个实例起作用,但是还没有碰巧使用该实例的任何状态。例如,”在这种情况下,我很喜欢您对“实例”的使用。
PaulBinder

56

将方法标记为static类可以很明显地表明它不使用任何实例成员,这有助于在浏览代码时有所帮助。

您不必将其移至另一个类,除非它是由概念上紧密相关的另一个类共享。


22

我确定这不会在您的情况下发生,但是我在某些代码中看到的一种“难闻的气味”使我不得不通过维护来使用大量的静态方法。

不幸的是,它们是假定特定应用程序状态的静态方法。(为什么可以确定,每个应用程序只能有一个用户!为什么User类不能在静态变量中跟踪该用户?)它们是访问全局变量的美化方法。他们也有静态构造函数(!),这几乎总是一个坏主意。(我知道有两个合理的例外)。

但是,当静态方法排除实际上不依赖于对象实例状态的域逻辑时,它们非常有用。它们可以使您的代码更具可读性。

只要确保将它们放在正确的位置即可。静态方法是否会干扰其他对象的内部状态?可以很好地说明他们的行为属于这些类之一吗?如果您没有适当地分离问题,那么以后您可能会头疼。


4
您的问题是静态字段/属性,而不是静态方法。
Asad Saeeduddin

10

有趣的是:

http://thecuttingledge.com/?p=57

ReSharper实际上并不建议您将方法设为静态。您应该问自己为什么该方法在该类中而不是在其签名中显示的类之一中...

但是这是reshaper文档所说的:http : //confluence.jetbrains.net/display/ReSharper/Member+can+be+made+static


2
我认为这一点被低估了。该工具真正告诉您的是,该方法仅对某些其他类的成员起作用。如果是某种命令(或“用例”或“交互器”)对象,谁负责操作其他对象,那很好。但是,如果它只处理一个听起来很像Feature Envy的其他类 。
格雷格

9

只是要添加到@Jason True的答案中,重要的是要认识到仅在方法上放置“静态”并不能保证该方法将是“纯”的。对于声明它的类而言,它将是无状态的,但是它可以很好地访问具有状态(应用程序配置等)的其他“静态”对象,这可能并不总是一件坏事,但这是造成以下情况的原因之一我个人倾向于使用静态方法,如果可以的话,如果它们是纯净的,则可以孤立地测试和推理它们,而不必担心周围的状态。


6

您应该在给定的情况下执行最易读和直观的操作。

性能参数不是一个好参数,除非在最极端的情况下,因为实际发生的唯一事情是this实例方法将一个额外的参数()压入了堆栈。


6

对于类中的复杂逻辑,我发现私有静态方法可用于创建孤立的逻辑,其中实例输入在方法签名中明确定义,并且不会发生实例副作用。所有输出必须通过返回值或out / ref参数进行。将复杂的逻辑分解为无副作用的代码块可以提高代码的可读性,并提高开发团队对此的信心。

另一方面,它可能导致一类被实用程序方法污染的类。像往常一样,逻辑命名,文档编制和团队编码约定的一致应用可以减轻这种情况。


5

ReSharper不会检查逻辑。它仅检查该方法是否使用实例成员。如果该方法是私有的,并且仅由(可能只是一个)实例方法调用,则这是使其成为实例方法的标志。


3

如果这些功能在许多页面上共享,则还可以将它们放在基本页面类中,然后使所有使用该功能的asp.net页面都继承自该功能(这些功能也可以是静态的)。


3

将方法设为静态意味着您可以从类外部调用该方法,而无需先创建该类的实例。在使用第三方供应商对象或附加组件时,这很有用。想象一下,如果您必须先创建一个控制台对象“ con”,然后再调用con.Writeline();。


Java将让您在调用con.Writeline()之前创建一个工厂实例来创建Console对象。
ScottMichaud

2

它有助于控制名称空间污染。


8
使方法静态化如何避免命名空间污染?
lockstock 2014年

1
从经验上讲,通过将方法与静态方法组合到类中,您避免了必须为所有可能与其他库或内置函数冲突的松散函数的“抓包”加上前缀的经验。使用静态方法,可以在类名(例如)下有效地对它们进行命名空间。Class.a_core_function( .. )vsa_core_function( .. )
lintuxvi

0

只是我的惊奇:将所有共享的静态方法添加到实用程序类中,使您可以添加

using static className; 

到您的using语句,这使代码的键入速度更快且更易于阅读。例如,在我继承的某些代码中,我有很多所谓的“全局变量”。我没有在实例类的类中创建全局变量,而是将它们全部设置为全局类的静态属性。如果杂乱无章,它就可以完成工作,而且我只能按名称引用属性,因为我已经引用了静态名称空间。

我不知道这是否是个好习惯。我有太多关于C#4/5的知识,还有太多要重构的旧代码,我只是想让Roselyn技巧指导我。

乔伊


0

希望您已经了解了静态方法和实例方法之间的区别。另外,答案可能很长,答案很短。其他人已经提供了长答案。

我的简短回答:是的,如果Resharper建议您可以将它们转换为静态方法。这样做没有害处。相反,通过使该方法静态化,实际上是在保护该方法,从而不必使任何实例成员都不必滑入该方法。这样,您可以实现OOP原则“ 最小化类和成员的可访问性 ”。

当ReSharper建议实例方法可以转换为静态方法时,它实际上是在告诉您:“为什么..此方法位于此类中,因为它实际上并未使用其任何状态?” 因此,它为您提供了深思的地方。然后,您可以意识到是否需要将该方法移至静态实用程序类。根据SOLID原则,一个班级应该只承担一个核心责任。因此,您可以通过这种方式更好地清理类。有时,即使在实例类中,您也确实需要一些辅助方法。如果是这种情况,您可以将它们保留在#region帮助器中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.