130

我的问题与静态方法与实例方法的性能特征及其可伸缩性有关。对于这种情况,假设所有类定义都在一个程序集中,并且需要多个离散指针类型。

考虑:

public sealed class InstanceClass
{
      public int DoOperation1(string input)
      {
          // Some operation.
      }

      public int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more instance methods.
}

public static class StaticClass
{
      public static int DoOperation1(string input)
      {
          // Some operation.
      }

      public static int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more static methods.
}

上面的类代表了一个辅助样式模式。

在实例类中,解析实例方法需要一些时间,这与静态类相反。

我的问题是:

  1. 当保持状态不是问题(不需要字段或属性)时,使用静态类总是更好吗?

  2. 与相同数量的实例类定义相比,如果有相当数量的这些静态类定义(例如 100 个,每个都有多个静态方法),这会对执行性能或内存消耗产生负面影响吗?

  3. 当调用同一个实例类中的另一个方法时,实例解析是否仍然发生?例如,在同一个实例中使用 [this]this.DoOperation2("abc")关键字DoOperation1

4

3 回答 3

175

理论上,由于额外的隐藏this参数,在所有其他条件相同的情况下,静态方法的性能应该略好于实例方法。

实际上,这几乎没有什么区别,以至于它会隐藏在各种编译器决策的噪音中。(因此,两个人可以“证明”一个人比另一个人更好,但结果不一致)。尤其是因为this通常在寄存器中传递并且通常在该寄存器中开始。

最后一点意味着,理论上,我们应该期望将对象作为参数并对其执行某些操作的静态方法比作为同一对象上的实例的等效方法稍差。尽管如此,差异是如此之小,以至于如果您尝试测量它,您可能最终会测量其他一些编译器决策。(特别是因为如果该引用一直在寄存器中的可能性也很高)。

真正的性能差异将归结为您是否人为地让内存中的对象来执行自然应该是静态的事情,或者您是否正在以复杂的方式将对象传递链纠缠起来以执行自然应该是实例的事​​情。

因此对于数字 1。当保持状态不是问题时,最好是静态的,因为这就是静态的。这不是性能问题,尽管有一个很好的编译器优化的总体规则 - 更有可能有人努力优化正常使用的案例而不是那些奇怪使用的案例。

2号。没有区别。每个成员都有一定的每类成本,包括有多少元数据,实际 DLL 或 EXE 文件中有多少代码,以及会有多少 jitted 代码。无论是实例还是静态,这都是一样的。

对于第 3 项,this是一样this的。但是请注意:

  1. 参数在this特定寄存器中传递。在同一个类中调用实例方法时,它可能已经在该寄存器中(除非它被隐藏并且由于某种原因使用了该寄存器),因此不需要任何操作来设置this它需要设置的内容. 这在一定程度上适用于例如方法的前两个参数是它进行的调用的前两个参数。

  2. 由于很明显它this不为空,因此在某些情况下可以使用它来优化调用。

  3. 由于很明显它this不为空,这可能会使内联方法调用再次更有效,因为为伪造方法调用而生成的代码可以省略一些它可能需要的空值检查。

  4. 也就是说,空检查很便宜!

值得注意的是,作用于对象的通用静态方法,而不是实例方法,可以减少http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-中讨论的一些成本the-associated-overheads/在给定类型没有调用给定静态的情况下。正如他所说,“顺便说一句,扩展方法是使泛型抽象更加付费的好方法。”

但是,请注意,这仅与该方法使用的其他类型的实例化有关,否则不存在。因此,它确实不适用于很多情况(其他一些实例方法使用该类型,其他一些其他代码使用该类型)。

概括:

  1. 大多数情况下,实例与静态的性能成本都可以忽略不计。
  2. 例如,当您滥用静态时,通常会产生什么成本,反之亦然。如果您不将它作为您在静态和实例之间做出决定的一部分,那么您更有可能获得正确的结果。
  3. 在极少数情况下,另一种类型中的静态泛型方法导致创建的类型比实例泛型方法少,这有时可以使其很少使用(并且“很少”指的是它在应用程序的生命周期,而不是它被调用的频率)。一旦您了解他在那篇文章中所说的内容,您就会发现它与大多数静态与实例决策 100% 无关。编辑:而且它主要只有 ngen 的成本,而不是 jitted 代码。

编辑:关于空检查有多便宜的说明(我在上面声称)。.NET 中的大多数 null 检查根本不检查 null,而是继续他们将要做的事情,假设它会起作用,如果发生访问异常,它会变成NullReferenceException. 因此,大多数情况下,当 C# 代码在概念上涉及空检查时,因为它正在访问实例成员,如果成功,成本实际上为零。一个例外是一些内联调用,(因为他们想要表现得好像他们调用了一个实例成员)并且他们只是点击一个字段来触发相同的行为,所以它们也非常便宜,而且它们仍然经常被排除在外(例如,如果该方法的第一步涉及按原样访问字段)。

于 2012-09-05T10:56:03.060 回答
12

当保持状态不是问题(不需要字段或属性)时,使用静态类总是更好吗?

我会说,是的。在声明某事static时,您声明了无状态执行的意图(这不是强制性的,而是人们期望的某种意图)

与相同数量的实例类相比,如果有相当数量的这些静态类(例如 100 个,每个都有多个静态方法),这是否会对执行性能或内存消耗产生负面影响?

不要这么想,除非你确定静态类真的是无字的,否则很容易弄乱内存分配并导致内存泄漏。

当 [this] 关键字用于调用同一个实例类中的另一个方法时,实例解析是否仍然发生?

不确定,关于一点(这纯粹是 CLR 的实现细节),但认为是的。

于 2012-09-05T10:37:43.543 回答
-1

静态方法更快,但 OOP 更少。如果您将使用设计模式,则静态方法可能是糟糕的代码。业务逻辑最好写成非静态的。文件读取、WebRequest 等常用函数作为静态函数更好。你的问题没有普遍的答案。

于 2012-09-05T10:40:23.973 回答