【问题标题】:Method can be made static, but should it?方法可以设为静态,但应该这样吗?
【发布时间】:2010-09-15 05:23:56
【问题描述】:

ReSharper 喜欢指出每个 ASP.NET 页面的多个功能,这些功能可以设为静态。如果我将它们设为静态,它对我有帮助吗?我应该将它们设为静态并将它们移动到实用程序类吗?

【问题讨论】:

标签: c# .net refactoring resharper static-methods


【解决方案1】:

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

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

【讨论】:

  • 我认为一个好的 linter 应该可以选择将消息限制为非虚拟方法,因为基类方法实际上什么都不做是很常见的。覆盖方法通常会做一些事情,但并非总是如此。有时为空 iEnumerable 之类的东西创建一个类很有用,它的方法基本上忽略了实例,但需要实例选择正确的方法才能使用。
  • "有时有些方法在逻辑上作用于一个实例,但还没有碰巧使用任何实例的状态。例如"我喜欢你在这个实例中使用 "for instance"。
【解决方案2】:

静态方法与实例方法 C# 语言规范的
Static and instance members 解释了差异。一般来说,静态方法可以提供比实例方法非常小的性能增强,但仅限于一些极端情况下(有关更多详细信息,请参阅this answer)。

FxCop 或代码分析状态中的规则 CA1822:

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

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

【讨论】:

  • > 编译器将向这些成员发出非虚拟调用站点实际上是“编译器可能发出......”。我记得 C# 编译器使用 callvirt 而不是 call 来解决一些潜在的错误。
  • 我直接从 FxCop 1.36 剪切并粘贴它。如果 FxCop 错了,那还算公平。
  • @Maxim 不确定我是否欣赏“废话”声明;接近陌生人的非常粗鲁的方式。但是,基本点是有效的;我已经更新了一些东西(那是 9 年前,所以我不记得我最初声明的基础了)。
  • @Maxim 您的观点无效。我向你保证,9 年前我的评价并不高。我很欣赏指出错误(或纠正错误的编辑)的 cmets,但不要粗鲁,也不要对他人寄予不合理的期望。不要称某事为“废话”;它暗示着一种欺骗的意图,而不是诚实的善良错误或无知。这很粗鲁。我自愿花时间在这里提供帮助,当它受到不尊重时,真的感觉毫无意义。不要告诉我要被什么冒犯——那是我的选择,不是你的。了解如何以尊重和正直的态度表达自己的观点。谢谢。
  • @Maxim 虽然委托不存储在每个实例旁边,但无状态类的每个实例确实会占用堆上的一些内存,这是无用的开销。通常实例化服务不是应用程序中的热门路径,但如果您的应用程序最终构建了大量此类对象,则会增加 GC 压力,而这可以通过简单地使用静态方法来避免。 OP 的原始声明,即在极端情况下静态方法比无状态实例提供性能优势,是适当的细微差别和有效的。
【解决方案3】:

在类中将方法标记为static 可以明显看出它不使用任何实例成员,这有助于在浏览代码时了解。

您不必将其移至另一个类,除非它打算由另一个密切相关的、概念方面的类共享。

【讨论】:

    【解决方案4】:

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

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

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

    请确保您将它们放在正确的位置。静态方法是否侵入性地操纵其他对象的内部状态?可以证明他们的行为属于其中一个类吗?如果你没有正确地分离关注点,你以后可能会头疼。

    【讨论】:

    • 你的问题是静态字段/属性,而不是静态方法。
    【解决方案5】:

    这很有趣:
    http://thecuttingledge.com/?p=57

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

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

    【讨论】:

    • 我认为这一点被低估了。该工具真正告诉您的是该方法仅对其他一些类的成员起作用。如果它是某种命令(或“用例”或“交互器”)对象,谁负责操作其他对象,那很好。但是,如果它只操作一个听起来很像 Feature Envy 的其他类。
    • 答案中的第一个链接已失效。
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        你应该在给定的场景中做最易读和最直观的事情。

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

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            【解决方案10】:

            如果函数在许多页面之间共享,您也可以将它们放在一个基页面类中,然后让所有使用该功能的 asp.net 页面都继承自它(这些函数也可以是静态的)。

            【讨论】:

              【解决方案11】:

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

              【讨论】:

              • Java 会让你在调用 con.Writeline() 之前创建一个工厂实例来创建 Console 对象。
              【解决方案12】:

              有助于控制命名空间污染。

              【讨论】:

              • 将方法设为静态如何帮助避免命名空间污染?
              • 从经验来看,通过将方法分组到具有静态方法的类中,您可以避免必须在可能与其他库或内置冲突的松散函数的所有“抓取包”前面加上前缀的体验职能。使用静态方法,它们在类名下有效地命名空间,例如。 Class.a_core_function( .. )a_core_function( .. )
              【解决方案13】:

              我希望您已经了解静态方法和实例方法之间的区别。此外,可以有一个很长的答案和一个简短的答案。其他人已经提供了长答案。

              我的简短回答:是的,您可以按照 ReSharper 的建议将它们转换为静态方法。这样做没有坏处。相反,通过使方法静态,您实际上是在保护该方法,以便您不会不必要地将任何实例成员滑入该方法。这样,您就可以实现 OOP 原则“尽量减少类和成员的可访问性”。

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

              【讨论】:

                【解决方案14】:

                Just my tuppence:将所有共享静态方法添加到实用程序类允许您添加

                using static className; 
                

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

                我不知道这是否是好的做法。我有很多关于 C# 4/5 的知识要学习,还有很多遗留代码要重构,所以我只是想让 Roselyn 技巧指导我。

                乔伊

                【讨论】:

                  猜你喜欢
                  • 2010-10-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-07-25
                  • 2011-04-11
                  • 2014-04-12
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多