有没有办法强制在C#中初始化静态字段?

请考虑以下代码:

class Program { static Program() { Program.program1.Value = 5; } static List values = new List(); int value; int Value { get { return value; } set { this.value = value; Program.values.Add(this); } } static Program program1 = new Program { value = 1 }; static Program program2 = new Program { value = 2 }; static Program program3 = new Program { value = 3 }; static void Main(string[] args) { if (Program.values.Count == 0) Console.WriteLine("Empty"); foreach (var value in Program.values) Console.WriteLine(value.Value); Console.ReadKey(); } } 

它只打印数字5,如果删除静态构造函数中的代码,则打印“Empty”。

有没有办法强制静态字段初始化,即使还没有使用?

我需要一个名为Values的静态属性,返回引用类型的所有实例。

我尝试了这些代码的一些变体,有些适用于某些类型,但不适用于其他类型。

编辑:上面的示例被破坏,尝试这一个:

 class Subclass { static Subclass() { Values = new List<Subclass>(); } public Subclass() { if (!Values.Any(i => i.Value.Equals(this.Value))) { Values.Add(this); } } public T Value { get; set; } public static List<Subclass> Values { get; private set; } } class Superclass : Subclass { public static Superclass SuperclassA1 = new Superclass { Value = 1 }; public static Superclass SuperclassA2 = new Superclass { Value = 2 }; public static Superclass SuperclassA3 = new Superclass { Value = 3 }; public static Superclass SuperclassA4 = new Superclass { Value = 4 }; } class Program { static void Main(string[] args) { //Console.WriteLine(Superclass.SuperclassA1); //UNCOMMENT THIS LINE AND IT WORKS foreach (var value in Superclass.Values) { Console.WriteLine(value.Value); } Console.ReadKey(); } } 

你的问题的答案是“好吧,是的”。 但是,“强迫”它的两种方式之一就是你已经在做的事情。

语言规范中的相关部分是10.11静态构造函数 ,具体为:

“类的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下第一个事件触发:

  • 创建了一个类的实例。
  • 引用该类的任何静态成员。

如果一个类包含执行开始的Main方法(第3.1节),则该类的静态构造函数在调用Main方法之前执行。 如果一个类包含带有初始化器的任何静态字段,那么这些初始化器在执行静态构造函数之前立即以文本顺序执行。“

但是你永远不会设置属性 – 而是直接设置支持字段,因此在创建program1,program2和program3时不要通过逻辑添加到静态列表。

即你需要改变:

  static Program program1 = new Program { value = 1 }; static Program program2 = new Program { value = 2 }; static Program program3 = new Program { value = 3 }; 

至:

  static Program program1 = new Program { Value = 1 }; static Program program2 = new Program { Value = 2 }; static Program program3 = new Program { Value = 3 }; 

在这种情况下,实际上有一种方法可以强制初始化属性。 此更改需要向基类添加一个类型参数,以表示将包含要初始化的字段的基类的未来子类。 然后我们可以使用RuntimeHelpers.RunClassConstructor来确保初始化子类静态字段。

以下将产生您要查找的结果:

 class Subclass { static Subclass() { Values = new List>(); // This line is where the magic happens System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle); } public Subclass() { if (!Values.Any(i => i.Value.Equals(this.Value))) { Values.Add(this); } } public T Value { get; set; } public static List> Values { get; private set; } } class Superclass : Subclass { public static Superclass SuperclassA1 = new Superclass { Value = 1 }; public static Superclass SuperclassA2 = new Superclass { Value = 2 }; public static Superclass SuperclassA3 = new Superclass { Value = 3 }; public static Superclass SuperclassA4 = new Superclass { Value = 4 }; } public class Program { public static void Main() { foreach (var value in Superclass.Values) { Console.WriteLine(value.Value); } Console.ReadKey(); } } 

发生的事情是,对RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle)的调用RuntimeHelpers.RunClassConstructor(typeof(TSubclass).TypeHandle)的静态构造函数在尚未运行时执行。 这可以确保静态字段首先按照此行从https://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx初始化:

如果一个类包含带有初始化程序的任何静态字段,那么这些初始化程序将在执行静态构造函数之前立即以文本顺序执行。

这是一个dotnetfiddle,展示它的工作原理:

https://dotnetfiddle.net/MfXzFd

实际上看起来你拼错了’价值’ – >’价值’所以:

  static Program program1 = new Program { Value = 1 }; static Program program2 = new Program { Value = 2 }; static Program program3 = new Program { Value = 3 }; 

漂亮打印更多线条

第二个示例不起作用,因为ValueSubclass的静态成员。

C#语法允许Superclass.Values ,但最终编译的方法调用将是Subclass.Values getter。 所以类型Superclass永远不会被触及。 另一方面, Superclass.SuperclassA1会触摸该类型并触发静态初始化。

这就是为什么C#没有真正的隐式静态初始化,你需要像MEF和Unity这样的combobox架。