关于c#:使用私有静态方法的优点

Advantages to Using Private Static Methods

当创建一个具有内部私有方法(通常是为了减少代码重复)的类(不需要使用任何实例字段)时,将该方法声明为静态方法是否具有性能或内存优势?

例子:

1
2
3
4
5
6
foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element,".//first");
    string second = GetInnerXml(element,".//second");
    string third = GetInnerXml(element,".//third");
}

1
2
3
4
5
6
7
8
9
10
private static string GetInnerXml(XmlElement element, string nodeName)
{
    return GetInnerXml(element, nodeName, null);
}

private static string GetInnerXml(XmlElement element, string nodeName, string defaultValue)
{
    XmlNode node = element.SelectSingleNode(nodeName);
    return node == null ? defaultValue : node.InnerXml;
}

将getInnerXML()方法声明为静态方法有什么好处吗?没有意见回复,我有意见。


从fxcop规则页面:

After you mark the methods as static, the compiler will emit non-virtual call sites to these members. Emitting non-virtual call sites will prevent a check at runtime for each call that ensures that the current object pointer is non-null. This can result in a measurable performance gain for performance-sensitive code. In some cases, the failure to access the current object instance represents a correctness issue.


当我编写一个类时,大多数方法分为两类:

  • 使用/更改当前实例状态的方法。
  • 帮助器方法不使用/更改当前对象的状态,但帮助我计算其他地方需要的值。

静态方法很有用,因为只要查看它的签名,就知道调用它不会使用或修改当前实例的状态。

举个例子:

1
2
3
4
5
6
7
public class Library
{
    private static Book findBook(List<Book> books, string title)
    {
        // code goes here
    }
}

如果一个图书馆状况的例子被搞砸了,而我正试图找出原因,我可以从它的签名中排除被发现的图书是罪魁祸首。

我尽量使用方法或函数的签名进行通信,这是一种很好的方式。


对静态方法的调用生成Microsoft中间语言(MSIL)中的调用指令,而对实例方法的调用生成callvirt指令,该指令还检查空对象引用。然而,在大多数情况下,两者的性能差异并不显著。

src:msdn-http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.110).aspx


是的,编译器不需要将隐式this指针传递给static方法。即使您不在实例方法中使用它,它仍然在被传递。


因为没有传递这个参数,所以速度会稍微快一点(尽管调用这个方法的性能成本可能比节省的成本要高得多)。

我想我能想到的私有静态方法的最好原因是它意味着你不能意外地更改对象(因为没有这个指针)。


这就迫使您记住,还要将函数使用的任何类范围的成员声明为静态成员,这样可以节省为每个实例创建这些项的内存。


我非常喜欢所有私有方法都是静态的,除非它们真的不能是静态的。我更喜欢以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyClass
{
    private readonly MyDependency _dependency;

    public MyClass(MyDependency dependency)
    {
        _dependency = dependency;
    }

    public int CalculateHardStuff()
    {
        var intermediate = StepOne(_dependency);
        return StepTwo(intermediate);
    }

    private static int StepOne(MyDependency dependency)
    {
        return dependency.GetFirst3Primes().Sum();
    }

    private static int StepTwo(int intermediate)
    {
        return (intermediate + 5)/4;
    }
}

public class MyDependency
{
    public IEnumerable<int> GetFirst3Primes()
    {
        yield return 2;
        yield return 3;
        yield return 5;
    }
}

访问实例字段的每个方法。为什么会这样?因为这个计算过程变得更加复杂,并且类最终得到了15个私有助手方法,所以我真的希望能够将它们拉出到一个新的类中,这个类以语义上有意义的方式封装了步骤的一个子集。

MyClass因为需要日志记录和通知Web服务而获得更多依赖项(请原谅陈词滥调的例子),那么很容易看到哪些方法具有哪些依赖项是非常有用的。

像R这样的工具可以让您通过几次击键从一组私有静态方法中提取类。当所有私有助手方法都与实例字段紧密耦合时,尝试这样做,您会发现这可能是一个非常头疼的问题。


如前所述,静态方法有许多优点。但是,请记住,它们将在应用程序的生命周期中一直存在于堆中。我最近花了一天时间在一个Windows服务中查找内存泄漏…泄漏是由实现IDisposable的类内的私有静态方法引起的,并且始终从using语句调用该方法。每次创建该类时,堆上都为该类中的静态方法保留了内存,不幸的是,当释放该类时,静态方法的内存没有释放。这导致此服务的内存占用在几天内消耗服务器的可用内存,并产生可预测的结果。