*可以*是静态的C#方法应该是静态的吗?[关闭]


103

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

我们今天在讨论这个问题,我有点担心。想象一下,您有一个很长的方法需要重构几行。新方法可能会从父方法中获取一些局部变量并返回一个值。这意味着它可能是静态的。

问题是:它应该是静态的吗?它在设计或选择上不是静态的,仅就其本质而言,因为它不引用任何实例值。



41
“非常准确”是矛盾的话题
马特·布里格斯

1
Resharper(Visual Studio工具工具)说是!:-)
丽贝卡

@Junto也许在您的版本中,但是我的说法可以设为静态...
Robbie Dee

Answers:


59

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

  1. 静态的方法,因为它们可以是
  2. 静态方法,因为它们必须

在中小型代码库中,您实际上可以将两种方法互换使用。

如果您有一个属于第一类的方法(可以是静态的),并且需要将其更改为访问类状态,那么找出是否有可能将静态方法转换为实例方法是相对简单的。

但是,在庞大的代码库中,大量的调用站点可能会进行搜索,以查看是否有可能将静态方法转换为非静态方法而代价太高。很多时候人们会看到呼叫数量,然后说:“好吧。我最好不要更改此方法,而要创建一个满足我需要的新方法”。

这可能会导致:

  1. 很多代码重复
  2. 方法参数数量激增

这两件事都是不好的。

因此,我的建议是,如果您的代码库超过200K LOC,则仅当方法必须是静态方法时,我才将它们设为静态。

从非静态到静态的重构相对容易(只需添加一个关键字),因此如果您想稍后将可静态化为实际的静态(当您需要实例外部的功能时),则可以。但是,将可能静态的实例化方法进行逆向重构要昂贵得多。

对于大型代码库,最好是从易于扩展的角度出发,而不是从思想的纯正角度出发。

因此,对于大型项目,除非需要它们,否则不要将其静态化。对于小型项目,请尽其所能。


43

不会将其设为该类公共静态成员。原因是使它成为公共静态是在谈论类的类型:不仅“该类型知道如何执行此行为”,而且“执行此行为是该类型的责任”。而且很有可能的是,行为不再与较大的类型有任何实际关系。

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

为了方便起见,您至少可以对其进行标记internal。如果您无法轻松访问更合适的类型,通常可以避免移动方法,但是仍然可以在需要的地方使用该方法,而该方法不会显示为类用户的公共接口。 。


6
同意 在这种情况下,我通常将方法设为“私有静态”。
harpo

6
我也不会假设私有静态。主要的问题是问自己:此方法在逻辑上是否适合该类型,或者只是为了方便起见而已?如果是后者,它是否可能更适合其他地方?
Joel Coehoorn

1
如:“新方法在逻辑上可以属于其他地方吗?”-线索是它不依赖于本地状态,也许返回类型是它所属的地方?
2011年

20

不必要。

将公共方法从静态更改为非静态是一项重大更改,并且需要更改所有调用方或使用方。如果一个方法看起来像一个实例方法,但是碰巧不使用任何实例成员,那么我建议您将它设为一个实例方法,以用于将来的检验。


我认为他主要是指私有方法
George Mauer

@Ray-对,这仅适用于非私人成员。我更新了答案以反映这一点。
迈克尔

我的想法完全是-仅因为您可以使某些内容静态化,并不意味着您必须或应该....
marc_s

那么,对于可以设为静态的私有方法,您将怎么做呢?

@Ray-可能照原样离开,直到我有足够的理由转向静态为止。
迈克尔

13

是。“它可以是静态的”的原因是它不能在调用它的对象的状态下进行操作。因此,它不是实例方法,而是类方法。如果它可以执行所需的操作而又不访问该实例的数据,则它应该是静态的。


11

是的,应该。有多种耦合度量标准可以衡量您的类如何依赖其他事物,例如其他类,方法等。将方法设置为静态是一种降低耦合程度的方法,因为您可以确定静态方法不会引用任何方法。成员。


7
不必要。静态方法将不会访问实例信息,不会像您所说的那样访问任何成员,但是它能够与其他类以及另一个通用方法进行交互。保持静态并不意味着必须减少耦合。但是,我理解您的意思。
Victor Rodrigues

相反,静态实际会增加耦合,因为您仅限于该静态方法,例如无法覆盖它,因此也无法更改其行为。
HimBromBeere

8

我认为如果将其标记为静态,它将使它更具可读性...然后有人会知道它不需要读取整个函数就不会引用任何实例变量...


6

就个人而言,我非常喜欢无国籍状态。您的方法是否需要访问类的状态?如果答案为“否”(可能为“否”,否则您不会考虑将其设为静态方法),那么,继续吧。

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

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

是的,一定要这样做。


1
静态方法仍然可以访问状态。它只是从传递给它的对象中获取状态。相反,实例字段不一定包含可变状态。
约尔根·福格

6

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


3
从技术上讲,这是正确的,但在实际意义上却不是。静态/非静态之间的差异非常很少会影响性能。
Foredecker

22
-1:教科书过早优化。在一般情况下,确定静态与非静态时,速度当然不应成为首要标准。
不确定2009年

2
同意不确定,这应该是您考虑静态与否的最后一个理由。
塞缪尔

5
我的意思是,在99%的情况下,速度是决定功能是静态还是非静态的最糟糕的原因之一。在面向对象设计方面,还有其他一些重要的考虑因素。
不确定2009年

2
@agnieszka:通常使用实例方法而不是静态方法的特殊原因是状态。如果您使方法保持静态,则在复杂的应用程序中,您将引入错误,使您难以为继。想想修改全局状态,竞争条件,线程安全等
桑德尔Davidhazi

5

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

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

因此,是的,使代码快速运行可能很重要,如果使方法静态化,这会发生,但是通常更重要的是使代码也尽可能不产生错误。一种实现方法是让您的代码尽可能少地访问“私有内容”。

乍看之下,这似乎无关紧要,因为OP显然是在谈论这种情况下重构不会出错并创建任何新错误的重构,但是这种重构代码必须在将来进行维护和修改,这会使您的代码遭受更大的“攻击”如果有访问私有实例成员的权限,则针对新的错误”。因此,总的来说,我认为这里的结论是“是的,大多数情况下,您的方法应该是静态的”,除非有其他原因使它们不是静态的。这仅仅是因为它是“更好地使用封装和数据隐藏并创建'更安全的'代码”。


4

仅仅因为可以就将其静态化不是一个好主意。静态方法应因其设计而不是偶然性而为静态。

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

话虽如此,听起来您正在为该类创建一个私有实用程序函数,而该函数实际上是设计静态的。


4

如果您能够重构出几行并且结果方法可能是静态的,则可能表明从该方法中拔出的行根本不属于包含类,因此您应该考虑将其移入自己的课。


2

就个人而言,我别无选择,只能将其设置为静态。在这种情况下,Resharper会发出警告,并且我们的PM拥有“ Resharper不会发出警告”的规则。


1
您可以更改ReSharper设置:P
Bernhard Hofmann,

1
如果这么简单,我们就没有Resharper的麻烦了……)
Prankster

1

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


是的,但是如果有一天您需要执行此操作,则可以始终将其设置为非静态。还是我错过了什么?

1
@Ray-我在回答中提到了这一点。从静态到非静态是一个重大变化,因此所有使用者都需要进行修改。对于一个小程序来说,这没什么大不了的,但是如果它是供其他人使用的库,则必须考虑到这一点。
Michael

所有调用站点都必须从静态方法调用更改为成员函数调用。
Brian Ensink 09年

抱歉,您在我的评论后添加了它。在我看来,我正在考虑私有方法,但这是公共方法的正确点。

...或者有一天您可以将其丢弃。这些参数不足以使我使方法成为非静态方法。
agnieszka,2009年

1

我建议最好的考虑方法是:如果您需要一个在没有实例化该类的实例或保持某种全局状态时需要调用的类方法,那么使用static是一个好主意。但总的来说,我建议您最好让成员成为非静态成员。


1

您应该考虑一下您的方法和类:

  • 您将如何使用它们?
  • 您需要从代码的不同级别获得大量访问权限吗?
  • 这是我可以在几乎所有可想到的项目中使用的方法/类吗?

如果最后两个为“是”,则您的方法/类可能应该是静态的。

最常用的示例可能是Math类。每种主要的OO语言都有它,并且所有方法都是静态的。因为您需要能够在任何地方,任何时间而不使用实例的情况下使用它们。

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

码:

public static void Reverse(Array array)

它甚至不返回任何内容,您的数组是反向的,因为所有数组都是Array类的实例。


数学是一个不好的例子。我想这更好:newnumber = number.round(2)比newnumber = Math.round(number)
涂鸦

3
Math类是使用静态类的完美示例。这就是本主题的内容,而不是您希望如何对数字

1

只要将新方法设为私有静态,这并不是一个重大更改。实际上,FxCop将此指南作为其规则之一(http://msdn.microsoft.com/en-us/library/ms245046(VS.80).aspx)进行了包含以下信息:

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

话虽如此,戴维·基恩(David Kean)的第一条评论更简洁地总结了这些担忧,他说这实际上更多是关于正确而不是关于性能提升:

尽管此规则被归类为性能问题,但是使方法静态化的性能提高仅为1%左右。而是,它是一个正确性问题,可能表明该成员不使用其他实例成员而导致成员不完整或存在错误。将方法标记为静态(在Visual Basic中为Shared)可以清楚地表明其不接触实例状态的意图。


1

出于不同的原因,我绝对会将我能变成的东西变成静态的:

进行JIT调用时,静态函数将不带“ this”参数。举例来说,这意味着3参数的非静态函数(成员方法)将以4个参数推入堆栈。

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


1

我正处于“仅使私有方法变为静态”阵营。公开方法可能会引入不需要的耦合,并且可能会降低可测试性:您不能对公共静态方法进行存根。

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


1

由于某种原因使之成为非静态的固有静态方法只是令人讨厌。以机智:

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

我打电话给我的银行,询问他们的邮寄地址。
他们要求我的帐号。
WTF?失败-应该是静态方法。


1
如果不同的分支机构为不同的客户提供服务怎么办?最终,将信件邮寄到银行总行可能会将其发送到适当的分支机构,但这并不意味着通过使用为客户服务的特定分支机构的地址来更好地服务于客户。
supercat

0

我通常从纯功能的功能角度来看它。是否需要是实例方法?如果没有,您可能会从强制用户传递变量而不破坏当前实例的状态中受益。(嗯,您仍然可以弄乱状态,但是重点是设计使然,而不是这样做。)我通常将实例方法设计为公共成员,并尽力使私有成员成为静态成员。如有必要(随后您可以更轻松地将它们提取到其他类中。


-1

在那些情况下,我倾向于将方法移到静态或utils库中,因此我不会将“对象”的概念与“类”的概念混合在一起

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.