使用私有静态方法的优点

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

例子:

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

113331 次浏览

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

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

由于没有传递this参数,它会稍微快一些(尽管调用该方法的性能成本可能比节省的成本要高得多)。

我想说,我能想到的私有静态方法的最好理由是,它意味着你不能意外地改变对象(因为没有this指针)。

从this的FxCop规则页面中:

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

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

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

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

举个例子:

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

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

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

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

来源:MSDN - https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/79b3xss3 (v = vs.110)

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

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方法都与实例字段紧密耦合时尝试这样做,您会发现这非常令人头疼。

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