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

Posted

技术标签:

【中文标题】*可以*为静态的C#方法应该是静态的吗? [关闭]【英文标题】:Should C# methods that *can* be static be static? [closed] 【发布时间】:2010-10-18 10:06:39 【问题描述】:

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

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

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

【问题讨论】:

与***.com/questions/169378/…相当完全相同 “相当准确”是矛盾的说法 Resharper,Visual Studio 工具中的工具,说是的! :-) @Junto 也许在您的版本中,但我说 可以 设为静态... 【参考方案1】:

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

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

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

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

但是,在大型代码库中,调用站点的绝对数量可能会导致搜索是否可以将静态方法转换为非静态方法的成本太高。很多时候人们会看到调用的数量,然后说“好的……我最好不要更改此方法,而是创建一个新的方法来满足我的需要”。

这可能导致:

    大量代码重复 方法参数数量激增

这两件事都很糟糕。

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

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

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

因此,对于大型项目,除非您需要,否则不要让事情变得静态。对于小型项目,只做你最喜欢的。

【讨论】:

【参考方案2】:

我会使它成为那个类public静态成员。原因是,将其设为 public static 是在说明类的类型:不仅“这种类型知道如何执行此行为”,而且“执行此行为是该类型的责任”。而且很可能该行为与较大的类型不再有任何实际关系。

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

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

【讨论】:

同意。在这种情况下,我通常将方法设为“私有静态”。 我也不会假设私有静态。主要是问自己:这种方法在逻辑上是否适合这种类型,还是只是为了方便起见?如果是后者,会不会更适合其他地方? 喜欢:“新方法在逻辑上是否属于其他地方” - 线索是它不依赖本地状态,也许返回类型是它所属的地方?【参考方案3】:

不一定。

将公共方法从静态转移到非静态是一项重大更改,需要对所有调用者或使用者进行更改。如果一个方法看起来像一个实例方法,但碰巧没有使用任何实例成员,我建议将它作为一个实例方法来作为未来证明的衡量标准。

【讨论】:

我认为他主要指的是私有方法 @Ray - 对,这仅适用于非私人会员。我更新了我的答案以反映这一点。 我的想法完全正确 - 仅仅因为你可以制作静态的东西并不意味着你必须或应该..... 那么对于可以设为静态的私有方法你会怎么做呢? @Ray - 可能会保持原样,直到我有足够的理由转向静态。【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

不一定。静态方法不会访问实例信息,没有成员,就像你说的那样,但它能够与其他类以及另一个常用方法进行交互。保持静态并不意味着必然会减少耦合。不过,我明白你的意思。 取而代之的是静态实际增加耦合,因为您仅限于该静态方法,例如无法覆盖它,因此无法更改 行为。【参考方案6】:

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

【讨论】:

【参考方案7】:

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

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

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

所以,是的,去吧,无论如何。

【讨论】:

静态方法仍然可以访问状态。它只是从传递给它的对象中获取状态。相反,实例字段不一定包含可变状态。 静态方法可以有自己的类的静态字段,因此是有状态的。这就像你所说的,只有你遵守这样的协议,而不是让它成为有状态的。【参考方案8】:

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

【讨论】:

这在技术上是正确的,但不是真正的物质意义。静态/非静态之间的差异很少会成为影响性能的因素。 -1:教科书过早优化。在决定一般情况下的静态与非静态时,速度当然不应该是首要标准。 同意不确定,这应该是您考虑静态与非静态的最后一个理由。 我的观点是,在 99% 的情况下,速度是决定一个函数是静态还是非静态的最糟糕的原因之一。当涉及到其他帖子详细阐述的 OO 设计时,还有更重要的考虑因素。 @agnieszka:一般使用实例方法而不是静态方法的特殊原因是状态。如果你让你的方法保持静态,在一个复杂的应用程序中,你会引入一些让你大吃一惊的错误。考虑修改全局状态、竞争条件、线程安全等。【参考方案9】:

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

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

所以,是的,如果您将方法设为静态,那么让您的代码快速运行可能很重要,但通常更重要的是让您的代码也尽可能地不能产生错误。实现这一目标的一种方法是让您的代码尽可能少地访问“私有内容”。

乍一看这似乎无关紧要,因为 OP 显然是在谈论重构,在这种情况下不会出错并产生任何新的错误,但是这个重构的代码必须在未来维护和修改,这使得你的代码有一个如果它可以访问私有实例成员,则关于新错误的更大“攻击面”。所以总的来说,我认为这里的结论是“是的,你的方法大部分应该是静态的”,除非有任何其他原因不让它们保持静态。而这仅仅是因为它“更好地利用封装和数据隐藏并创建'更安全'的代码”......

【讨论】:

【参考方案10】:

仅仅因为你可以做一些静态的事情并不是一个好主意。静态方法应该是静态的,因为它们的设计,而不是偶然的。

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

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

【讨论】:

是的,但如果有一天你需要这样做,你可以让它不是静态的。还是我错过了什么? @Ray - 我在回答中提到了这一点。静态到非静态是一个突破性的变化,所以所有的消费者都需要修改。对于一个小程序来说没什么大不了的,但是如果它是一个供他人使用的库,则必须考虑到这一点。 所有调用点都要从静态方法调用改为成员函数调用。 对不起,你在我的评论之后添加了这个。在我看来,我在考虑私有方法,但这对于公共方法来说是一个有效的观点。 ...或者有一天你可以把它扔掉。这些论点不足以让我将方法设为非静态。【参考方案13】:

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

【讨论】:

您可以更改 ReSharper 设置:P 如果它这么简单,我们就没有 Resharper 的磨损......永远:)【参考方案14】:

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

【讨论】:

【参考方案15】:

你应该考虑你的方法和类:

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

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

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

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

代码:

public static void Reverse(Array array)

它甚至不返回任何东西,你的数组被反转了,因为所有数组都是 Array 类的实例。

【讨论】:

数学是一个不好的例子。我想这更好: newnumber = number.round(2) 比 newnumber = Math.round(number) Math 类是使用静态类的完美示例。这就是本主题的内容,而不是关于您喜欢如何对数字进行四舍五入...【参考方案16】:

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

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

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

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

【讨论】:

【参考方案17】:

出于不同的原因,我肯定会将任何我能做的事情变成静态的:

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

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

【讨论】:

【参考方案18】:

我属于“仅将私有方法设为静态”阵营。创建公共方法可能会引入您不想要的耦合,并且可能会降低可测试性:您不能存根公共静态方法。

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

【讨论】:

【参考方案19】:

由于某种原因被制成非静态的固有静态方法简直令人讨厌。也就是说:

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

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

【讨论】:

如果不同的客户由不同的分支机构服务怎么办?将信件邮寄到银行总行可能最终会到达正确的分行,但这并不意味着使用为其提供服务的特定分行的地址不会更好地为客户提供服务。【参考方案20】:

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

【讨论】:

【参考方案21】:

在这些情况下,我倾向于将方法移至静态或实用程序库,因此我不会将“对象”的概念与“类”的概念混为一谈

【讨论】:

以上是关于*可以*为静态的C#方法应该是静态的吗? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

RestTemplate 应该是静态全局声明的吗?

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

“静态”模式不应该总是静态的吗?

纯方法必须是静态的吗?

用于 C/C++/C# 的免费静态代码扫描器 [关闭]