当方法可以是静态的但实际上不是时,ReSharper会发出警告。

68

为什么ReSharper会在一个方法可以变成静态方法时报错,但实际上没有?

是因为静态方法只创建一个实例(在类型上),从而提高性能吗?


2
有一个非常类似的问题是关于Java和Eclipse编译器选项的:https://dev59.com/3Ggu5IYBdhLWcg3wj3v5 不同的语言,相同的问题; =) - Samuel Rossille
这个检查的名称是“成员可以被静态化(共享)”,它有两个变体:“私有可访问性”和“非私有可访问性”。(在ReSharper 7.1中。)它列在ReSharper >选项>代码检查>检查严重程度>全部>常见实践和代码改进中。 - Rory O'Kane
这是针对ReSharper检查的操作菜单示例截图(尽管它是针对VB.NET代码的)。 - Rory O'Kane
8个回答

109

我认为这个评论非常有用,因为它指出了两个重要的问题:

  1. 它让我问自己,这个方法是否真的应该成为类型的一部分。由于它没有使用任何实例数据,所以至少要考虑它是否可以移动到自己的类型中。它是类型的一个组成部分,还是一个通用的实用方法?

  2. 如果将该方法保留在特定类型上确实有意义,那么存在潜在的性能提升,因为编译器将为静态方法生成不同的代码。


16
好的答案不仅仅关乎性能。Resharper 还为你提供一些设计反馈。 - serg10

25

以下是关于同一警告的FxCop文档描述(重点已添加):

如果成员不访问实例数据或调用实例方法,则可以将其标记为静态(Visual Basic中的共享)。当你将方法标记为静态时,编译器会向这些成员发出非虚拟调用站点。发出非虚拟调用站点将防止每次调用时在运行时进行检查,以确保当前对象指针非空。对于性能敏感的代码,这可以导致可衡量的性能提升。 在某些情况下,未访问当前对象实例表示一个正确性问题。


1
这是真的,但实际上并没有带来太大的性能提升。我会关闭那个警告。在开发过程中,ReSharper想要将所有东西都变成静态的,这很烦人。 - Chad Grant
3
这是正确的,但忽略了一个事实,那就是你此时不必创建对象来访问该方法...如果你不需要实例来完成其他任务,那么不需要创建对象将带来更大的性能提升。 - Charles Bretana
2
同意,性能提升通常相当小(对于大多数非平凡方法来说可以忽略不计)。正确性原因通常更令人担忧——如果该方法不特定于实例,则将其标记为静态可以清楚地表明这是有意的;如果该方法确实是特定于实例的,则警告会告诉您在实现中犯了错误。 - itowlson
1
@Chad:resharper并不是“想要”将事物变为静态的;它只是指出可以将其变为静态;你可以将其视为一个待办标记。如果你真的在争论“想要将_所有东西_都变成静态”……我会认为你的代码存在严重的设计问题。 - sehe
1
@sehe 希望/推荐...无论你如何解释这个词,Reshaper都会对此进行纠缠。我不知道你从我的评论中得出了什么,让你认为我个人让我的代码变得过于静态。太过激烈了吧? - Chad Grant
@ChadGrant:哈哈,干杯-啥时候这么晚了 :) 注意,我用斜体标出一个词以突出双曲线。因此,请再读一遍:“如果”……“真的”……“一切”……“我会”认为[...]。换句话说,我在开玩笑地指出不必要的夸张。 - sehe

6

这里有一个非常好的关于此主题的辩论 (SO)。我认为如果可以将方法设置为静态方法,就应该将其设置为静态方法。我之所以这样认为,是因为为什么会有一个不使用任何实例数据的实例方法呢?在这种情况下,它真的是一个实例方法吗,还是实际上是一个类方法?


5
如果将方法声明为静态的,则使用该方法时无需创建类的任何实例,这可以节省构造处理所必需的 CPU 周期、堆内存空间及垃圾回收器在从堆中回收对象时所需的 CPU 周期。
此外,您提出的问题似乎暗示针对实例方法,方法的代码会为创建的每个类实例重复执行。这是错误的。无论您为任何类型创建多少实例,方法的代码都仅加载到内存中一次。每个实例存储在堆上的对象仅存储类型的“状态”(非静态字段和一些其他跟踪变量)。

3

这不是抱怨,只是建议。


1
对我来说,这个ReSharper建议的最大好处(你可以将其设置为警告、建议或提示)是它鼓励我尽可能地使许多方法成为静态的。这是一件好事,因为静态方法没有直接依赖于它所属的类。这意味着它可以轻松地作为静态成员移动到另一个类中。
ReSharper中另一个很棒的技巧是通过使用“使方法静态”重构来使一组相关的方法成为静态方法。这将把一些依赖项移动到方法参数中。当您稍后查看这组方法时,您可能会发现它们都访问特定类型的特定对象。然后,您可以使用“使方法非静态”重构,并指定该对象为新的this指针。这将把您的方法移动到另一个类中。
从这个开始:
internal class ClassA
{
    public ClassB Property { get; set; }

    public int Method()
    {
        var classB = Property;
        return classB.Property1 + classB.Property2;
    }
}

internal class ClassB
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
}

转换为:

    public static int Method(ClassB property)
    {
        var classB = property;
        return classB.Property1 + classB.Property2;
    }

转换为:

internal class ClassA
{
    public ClassB Property { get; set; }
}

internal class ClassB
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }

    public int Method()
    {
        return Property1 + Property2;
    }
}

2
这意味着它可以轻松地作为静态成员移动到另一个类中。这也意味着你正在编写过程式而不是面向对象的代码,这是一件坏事。 - Evren Kuzucuoglu
@GFK:也许是,也许不是。一旦它是静态的,你就可以将它移动到另一个类中,并可能将其变成该类的实例成员。 - John Saunders

1
你不必将“this”推入函数的堆栈以用于静态方法。这也是它更便宜的另一个原因。

0

静态变量在第一次使用时实例化并保留在内存中。 如果不再使用,可能会成为问题。 静态变量更难进行测试(模拟等...)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接