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

例子:

foreach (XmlElement element in xmlDoc.DocumentElement.SelectNodes("sample"))
{
    string first = GetInnerXml(element, ".//first");
    string second = GetInnerXml(element, ".//second");
    string third = GetInnerXml(element, ".//third");
}

...

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()方法声明为静态有什么好处吗?请不要发表意见,我有自己的看法。


当前回答

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

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

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

举个例子:

public class Library
{
    private static Book findBook(List<Book> books, string title)
    {
        // code goes here
    }
}

如果库状态的实例发生了错误,并且我试图找出原因,那么仅从findBook的签名就可以排除它是罪魁祸首。

我尝试尽可能多地使用方法或函数的签名进行交流,这是一种很好的方式。

其他回答

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

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

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

举个例子:

public class Library
{
    private static Book findBook(List<Book> books, string title)
    {
        // code goes here
    }
}

如果库状态的实例发生了错误,并且我试图找出原因,那么仅从findBook的签名就可以排除它是罪魁祸首。

我尝试尽可能多地使用方法或函数的签名进行交流,这是一种很好的方式。

As has already been stated, there are many advantages to static methods. However; keep in mind that they will live on the heap for the life of the application. I recently spent a day tracking down a memory leak in a Windows Service... the leak was caused by private static methods inside a class that implemented IDisposable and was consistently called from a using statement. Each time this class was created, memory was reserved on the heap for the static methods within the class, unfortunately, when the class was disposed of, the memory for the static methods was not released. This caused the memory footprint of this service to consume the available memory of the server within a couple of days with predictable results.

从FxCop规则页面:

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

这迫使您记住将函数使用的任何类作用域成员也声明为静态的,这将节省为每个实例创建这些项的内存。

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

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个私有helper方法,那么我真的希望能够将它们拉出到一个新类中,以一种语义上有意义的方式封装步骤的子集。

当MyClass获得更多依赖时,因为我们需要日志记录,也需要通知web服务(请原谅这些陈词滥调的例子),那么很容易看到哪些方法有哪些依赖关系是很有帮助的。

像r#这样的工具可以让你从一组私有静态方法中提取一个类,只需几个按键。当所有私有helper方法都与实例字段紧密耦合时尝试这样做,您会发现这非常令人头疼。