首页 > 解决方案 > 需要对 C# 中的静态构造函数 VS 静态字段初始值设定项故事进行一些说明

问题描述

几天前,我问自己通过静态构造函数初始化静态字段和使用静态字段初始化器(在声明点内联初始化静态字段)之间的区别(如果有的话)。

在阅读了大量关于该主题的 stackoverflow 问题和著名的 Jon Skeet 关于 beforefieldinit 标志的文章之后,我现在对这两种初始化策略之间的区别有了更好的理解。

有一点我不确定,主要是因为我找不到任何关于它的官方文档。

静态构造函数保证只执行一次,我认为即使在多线程场景中也是如此(当不同的线程创建类的实例和/或使用类的静态成员时。无论如何,静态构造函数运行一次并且只有一次)。

即使对于静态字段的内联初始化也是如此吗?即使在多线程场景中,静态字段的内联初始化是否也能保证执行一次?

我仍然缺少的另一点是类的静态字段初始化的这种差异的实际后果是什么。换句话说,我想了解在声明点初始化静态内联(而不是使用静态构造函数)的选择何时会影响一段代码的正确性。

大多数时候(这主要取决于我通常处理的代码类型,即 Web 应用程序)我在服务类中使用静态只读字段来存储我正在编写的服务用于执行计算或做出决策的内容. 我决定将这些东西放在静态字段中,因为它们对于我正在编写的类的所有可能实例都需要相同,它们实际上是不属于特定实例的不变量,而是属于算法本身。

这是一个例子:

public class SomeInterestingService 
{
   private static readonly int ConstantNumber = 13;
   private static readonly string[] Names = new[] { "bob", "alice" };

   private readonly INumberGenerator numberGenerator;

   public SomeInterestingService(INumberGenerator numberGenerator)
   {
      this.numberGenerator = numberGenerator ?? throw new ArgumenNullException(nameof(numberGenerator));
   }

   public int ComputeMagicNumber() 
   {
      int answer = this.numberGenerator.GetNumber();

       foreach(var name in names)
       {
          answer += name.Length;
       }

       answer += ConstantNumber;
       return answer;
   }
}

在这样的代码中,选择静态构造函数初始化或静态字段的内联初始化是否有任何实际差异ConstantNumberNames除了性能差异(由于使用静态构造函数时无法进行的运行时优化,内联初始化性能更高) ?

在任何奇怪的极端情况下,上面代码的正确性会受到 coiche 的影响吗?(我想不是)

标签: c#.netstaticinitializationstatic-constructor

解决方案


原始问题:

在这样的代码中,选择静态构造函数初始化或静态字段 ConstantNumber 和 Names 的内联初始化是否有任何实际差异,除了性能差异(由于使用静态时无法进行的运行时优化,内联初始化性能更高构造函数)?

答案是不。这些属性要么在每次构造类时设置(实例属性),要么在第一次调用类的任何成员或方法时设置(静态属性)。

@Henk Holterman 所说的是因为名称数组是一种引用类型,理论上您可以更改数组中的任何值。喜欢:

Names[0] = "Henk Holterman";

即使该属性是只读的。这意味着,您不能将数组的新实例分配给该属性。数组中的值不是只读的。并且可以在公共情况下或通过调用该类的方法进行操作。


推荐阅读