C#中私有静态方法的用途是什么?

4
我有一个名为MyClass.cs的公共类。它有3个方法:
 public class MyClass
 {
    public IEnumerable<MyDto> PublicA(bool useCache = true)
    {
    //Call an external resource
    //some code
    }

    public IEnumerable<AnotherDto> PublicB()
    {
    //some code
    var x= MyPrivateMethod(input);
    //some code
    }

    private IEnumerable<AnotherDto> MyPrivateMethod(IEnumerable<SomeDto>)
    {
    //return Mapped data from IEnumerable<SomeDto> to  IEnumerable<AnotherDto>

    }
 }

我使用ReSharper作为重构工具。它建议对MyPrivateMethod使用static关键字。

private static IEnumerable<AnotherDto> MyPrivateMethod(IEnumerable<SomeDto>)

但是这个关键字的作用是什么呢?由于这个方法是私有的,不会被想要使用MyClass实例的其他类使用。

我进行了测试并发现,当我使用static关键字为MyPrivateMethod时,我无法调用该类中非私有静态方法。但是我仍然不知道这有什么用处?例如,是否有存储或时间优化的好处?


7
这段话的意思是:通过将方法声明为静态方法,可以清楚地表明它与任何特定实例的MyClass都没有关联。一般来说,如果一个方法在逻辑上与特定实例无关,则将其声明为静态方法可以更加清晰地表达这个意思。同时,这也意味着你可以从其他静态方法中调用它,而不必获取实例的引用。 - Jon Skeet
1
我测试并发现,当我对MyPrivateMethod使用static关键字时,我无法调用该类中任何其他非私有静态方法。那么你的测试是不完整的。private static方法可以访问类中的任何其他静态方法,无论它们是否为私有。 - JLRishe
你说过,“将其设为静态可以使其更清晰。” 你的意思是它只对代码审查有用,对性能没有任何影响吗?此外,你还说“你可以从其他静态方法调用它,而不必获取实例的引用”。但当该方法是私有的时候,我无法从其他类中访问它。 - Elnaz
ReSharper 发现您正在使用 MyPrivateMethod 来在类的其他方法之间共享代码,注意到该方法当前未访问任何非静态方法或字段,并建议将 static 添加到其声明中。它试图猜测您意图创建一个“帮助方法”,而不是驱动您的决策。 - Sergey Kalinichenko
@Elnaz,我不太明白你刚才说的话,但是是的,private static方法可以调用类中的任何其他静态方法。请参见此处:https://ideone.com/ijLhEr - JLRishe
无论是public还是privatestatic都是静态的。你误解了static,在static方法中不能调用任何非静态方法。链接 - Aria
1个回答

13
根据MSDN
未访问实例数据或调用实例方法的成员可以标记为静态(在Visual Basic中为Shared)。将方法标记为静态后,编译器将向这些成员发出非虚拟调用站点。发出非虚拟调用站点将防止在每个调用处运行时进行检查,以确保当前对象指针为非空。这可以为性能敏感的代码实现可衡量的性能增益。在某些情况下,无法访问当前对象实例表示正确性问题。

https://msdn.microsoft.com/en-us/library/ms245046.aspx

另一个好处是调用顺序,当您调用实例方法时,生成的代码将把this实例作为第一个参数推送到堆栈上,而方法的其余参数将被推送到堆栈上。因此,每个实例方法调用都需要额外推送一个堆栈以获取this和其他方法参数。
如果您将方法转换为静态方法,则静态方法调用不需要this,因此CPU少了一次推送操作。单次调用似乎并没有太大优势。
但是,如果您的方法将被频繁使用,并且如果您有几个不需要this的方法,则可以节省大量的CPU时间,特别是在图形和科学计算中。
这就是为什么当方法不引用任何属于this的内容时,Resharper建议您将方法更改为静态方法的原因。
以下是示例:
    public int Add(int a, int b) {
        return a + b;
    }

    public static int StaticAdd(int a, int b) {
        return a + b;
    }

    public void InstanceAdd() {
        Console.WriteLine(this.Add(3,3));
    }

    public void InstanceAddStatic()
    {
        Console.WriteLine(StaticAdd(3, 3));
    }

这是为调用“InstanceAdd”中的实例方法而生成的代码

.method public hidebysig 
    instance void InstanceAdd () cil managed 
{
    // Method begins at RVA 0x2095
    // Code size 16 (0x10)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldarg.0
    IL_0002: ldc.i4.3
    IL_0003: ldc.i4.3
    IL_0004: call instance int32 Temp.MathTest::Add(int32, int32)
    IL_0009: call void [System.Console]System.Console::WriteLine(int32)
    IL_000e: nop
    IL_000f: ret
} // end of method MathTest::InstanceAdd

这是在“StaticAdd”中生成实例方法的il。

.method public hidebysig 
    instance void InstanceAddStatic () cil managed 
{
    // Method begins at RVA 0x20a6
    // Code size 15 (0xf)
    .maxstack 8

    IL_0000: nop
    IL_0001: ldc.i4.3
    IL_0002: ldc.i4.3
    IL_0003: call int32 Temp.MathTest::StaticAdd(int32, int32)
    IL_0008: call void [System.Console]System.Console::WriteLine(int32)
    IL_000d: nop
    IL_000e: ret
} // end of method MathTest::InstanceAddStatic

如果您查看"StaticAdd",则没有ldarg.0,它代表this。对于每个方法调用,第一条指令始终是ldarg.0,然后是其余的参数。


1
我相信Resharper建议这样做的原因与在堆栈上没有this所节省的CPU开销无关。 - JLRishe
@AkashKava 不,我的意思是我不认为你描述的情况是Resharper建议这样做的“原因”,至少不是唯一的原因(你在最后一段声称这是Resharper的原因)。你有一些引用可以解释他们推荐这样做的原因吗? - JLRishe
@JLRishe 请查看规则描述,虽然这是MSDN,但我相信Resharper的开发人员也会遵循此规则集。 - Akash Kava
@AkashKava,那个MSDN页面上的解释与你回答中给出的原因完全无关。 - JLRishe
@JLRishe 是的,我同意,我已经更新了我的答案,并且还添加了示例以作为额外的好处。 - Akash Kava
显示剩余2条评论

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