* *可以*静态的C#方法应该是静态的吗?

时间:2009-04-08 20:49:05

标签: c# static methods

的C#方法是静态的吗?

我们今天正在讨论这个问题,而且我有点蠢蠢欲动。想象一下,你有一个很长的方法,你可以重构几行。新方法可能从父方法中获取一些局部变量并返回一个值。这意味着它可以是静态的。

问题是:应该它是静态的吗?它不是设计或选择的静态,仅仅是因为它不引用任何实例值。

21 个答案:

答案 0 :(得分:59)

这取决于。 实际上有两种类型的静态方法:

  1. 因为可以
  2. 而是静态的方法
  3. 因为必须
  4. 而是静态的方法

    在中小型代码库中,您可以互换地处理这两种方法。

    如果你的方法属于第一类(可以是静态的),并且你需要将其更改为访问类状态,那么可以相对直接地确定是否可以将静态方法转换为实例方法。

    然而,在大型代码库中,大量的调用站点可能会进行搜索,以查看是否可以将静态方法转换为非静态方法,而且成本过高。很多时候人们都会看到通话次数,然后说“好吧......我最好不要改变这种方法,而是创造一种能满足我需要的新方法。”

    这可能导致:

    1. 许多代码重复
    2. 方法参数数量激增
    3. 这两件事都很糟糕。

      所以,我的建议是,如果你的代码库超过200K LOC,那么我只会将方法设为静态,如果它们是必须是静态的方法。

      从非静态到静态的重构相对容易(只需添加一个关键字),所以如果你想稍后将一个可以静态化为一个实际的静态(当你需要它在一个实例之外的功能时)那么您可以。但是,反向重构将可以静态转换为实例方法的成本要高得多。

      对于大型代码库,最好是在易于扩展方面进行错误,而不是在思想纯度方面。

      所以,对于大项目来说,除非你需要,否则不要让事情变得静止。对于小型项目,只需做你最喜欢的事情。

答案 1 :(得分:43)

使其成为该类 public 静态成员。原因是使它成为公共静态是说类的类型:不仅“这种类型知道如何做这种行为”,而且“这种类型的责任是执行这种行为。”而且这种行为不再与更大的类型有任何真正的关系。

但这并不意味着我根本不会让它静止。问问自己:新方法在逻辑上属于别处吗?如果您对此回答“是”,您可能确实希望将其设置为静态(并将其移动)。即使这不是真的,你仍然可以使它静止。只是不要标记public

为方便起见,您至少可以标记internal。如果您无法轻松访问更合适的类型,这通常可以避免需要移动方法,但仍然可以在需要的地方访问它,以便它不会显示为类的用户的公共接口的一部分

答案 2 :(得分:20)

不一定。

将公共方法从静态方法移动到非静态方法是一项重大变革,需要对所有呼叫者或消费者进行更改。如果一个方法看起来像一个实例方法,但碰巧不使用任何实例成员,我建议将它作为实例方法作为未来验证的度量。

答案 3 :(得分:13)

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

答案 4 :(得分:12)

是的,它应该。有各种metrics of coupling可以衡量你的类如何依赖其他东西,比如其他类,方法等。使静态方法是一种保持耦合程度的方法,因为你可以确定静态方法不引用任何成员。

答案 5 :(得分:8)

我认为如果你把它标记为静态会使它更具可读性......然后有人会知道它不会引用任何实例变量而不必阅读整个函数......

答案 6 :(得分:6)

就个人而言,我是无国籍的忠实粉丝。您的方法是否需要访问类的状态?如果答案是否定的(并且可能没有,否则你不会考虑将其作为静态方法),那么,是的,去吧。

对国家的访问不那么令人头痛。正如隐藏其他类不需要的私有成员是个好主意一样,将状态隐藏在不需要它的成员中是个好主意。减少访问可能意味着更少的错误。此外,它使线程更容易,因为更容易保持静态成员线程安全。还有一个性能考虑因素,因为运行时不需要将对 this 的引用作为静态方法的参数传递。

当然,缺点是,如果您发现以前的静态方法由于某种原因必须访问该状态,那么您必须更改它。现在我明白这对于公共API来说可能是一个问题,所以如果这是公共类中的公共方法,那么也许您应该考虑一下这一点的含义。尽管如此,我从未在现实世界中遇到过这种情况,这实际上引起了一个问题,但也许我很幸运。

所以是的,无论如何都要去。

答案 7 :(得分:6)

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

答案 8 :(得分:4)

因为你可以做一些静态不是一个好主意。静态方法由于其设计应该是静态的,而不是由于偶然事件。

像迈克尔说的那样,稍后更改它会破坏正在使用它的代码。

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

答案 9 :(得分:4)

如果您能够重构几行并且生成的方法可能是静态的,则可能表明您从该方法中提取的行根本不属于包含类,您应该考虑将他们带入自己的班级。

答案 10 :(得分:4)

我很惊讶,事实上很少有人提到封装。实例方法将自动访问所有私有(实例)字段,属性和方法。除了从基类继承的所有受保护的。

编写代码时,应该编写代码,以便尽可能少地公开代码,以便尽可能少地访问代码。

所以,是的,如果你的方法是静态的,那么使你的代码变得很快就会很重要,但通常更重要的是让你的代码尽可能无法创建bug。实现这一目标的一种方法是让您的代码尽可能少地访问“私有内容”。

这看起来似乎无关紧要,因为OP显然是在讨论在这种情况下不会出错的重构并创建任何新的错误,但是这个重构的代码必须在将来维护并修改,这使得您的代码具有如果新漏洞可以访问私有实例成员,则会有更大的“攻击面”。所以一般来说我认为这里的结论是“是的,大多数你的方法应该是静态的”,除非有任何其他理由不让它们静止。这只是因为它“更好地利用封装和数据隐藏并创建'更安全'的代码”......

答案 11 :(得分:2)

我个人别无选择,只能让它变得静止。 Resharper在这种情况下发出警告,我们的PM有一条规则“没有来自Resharper的警告”。

答案 12 :(得分:1)

我建议考虑它的最佳方法是:如果你需要一个类方法,当没有类的实例被实例化时需要被调用,或者保持某种全局状态,那么静态是一个好主意。但总的来说,我建议你更喜欢让会员成为非静态的。

答案 13 :(得分:1)

这取决于但通常我不会将这些方法设为静态。代码总是在变化,也许有一天我会希望将该函数设置为虚拟并在子类中覆盖它。或者有一天它需要引用实例变量。如果必须更改每个呼叫站点,那么进行这些更改将更加困难。

答案 14 :(得分:1)

你应该考虑你的方法和课程:

  • 你打算如何使用它们?
  • 您是否需要从代码的不同级别对它们进行大量访问?
  • 这是一个我可以在几乎所有可以想象的项目中使用的方法/类。

如果最后两个是“是”,那么你的方法/类应该是静态的。

最常用的示例可能是Math类。每种主要的OO语言都有它,并且所有方法都是静态的。因为您需要能够随时随地使用它们,而无需创建实例。

另一个很好的例子是C#中的Reverse()方法。
这是Array类中的静态方法。它颠倒了数组的顺序。

代码:

public static void Reverse(Array array)

它甚至没有返回任何内容,因此所有数组都是Array类的实例。

答案 15 :(得分:1)

只要您使新方法私有静态,它就不是一个重大改变。实际上,FxCop将此指南作为其规则之一(http://msdn.microsoft.com/en-us/library/ms245046(VS.80).aspx)包含在内,并提供以下信息:

  

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

话虽如此,David Kean的第一条评论更简洁地总结了这些担忧,并说这实际上更多的是关于正确而不是绩效收益:

  

虽然此规则被归类为性能问题,但使方法静态的性能提升仅为1%左右。   相反,它更像是一个正确性问题,可以通过未使用其他实例成员来指示成员中的不完整或错误。将方法标记为静态(Visual Basic中的共享)可明确其不打触实例状态的意图。

答案 16 :(得分:1)

我肯定会因为不同的原因而把任何我变成静态的东西:

在没有“this”参数的情况下调用JIT时的静态函数。 这意味着,例如,一个3参数非静态函数(成员方法) 在堆栈上被4个参数推到一起。

编译为静态函数的相同函数将使用3个参数调用。 这可以释放JIT的寄存器并节省堆栈空间......

答案 17 :(得分:1)

我在“只做私人方法静态”阵营。制作公共方法可能会引入您不想要的耦合并可能降低可测试性:您不能存根公共静态方法。

如果你想对使用公共静态方法的方法进行单元测试,你最终也会测试静态方法,这可能不是你想要的。

答案 18 :(得分:1)

由于某种原因导致非静态的固有静态方法简直令人讨厌。即:

我打电话给我的银行并要求我的余额 他们要求我的账号。很公平。实例方法。

我致电我的银行并要求提供邮寄地址 他们要求我的帐号。
WTF?失败 - 应该是静态方法。

答案 19 :(得分:0)

我从纯功能的功能角度来看一般。它需要是一个实例方法吗?如果没有,您可能会受益于强制用户传入变量而不是破坏当前实例的状态。 (好吧,你仍然可以破坏状态,但重点是,按照设计,不要这样做。)我通常将实例方法设计为公共成员,并尽我所能使私有成员保持静态。如有必要(稍后您可以更容易地将它们提取到其他类中。

答案 20 :(得分:-1)

在这些情况下,我倾向于将方法移动到静态或utils库,所以我不会将“对象”的概念与“类”的概念混合