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

时间:2010-01-28 13:15:01

标签: c# static initialization

请考虑以下代码:

class Program
{
    static Program() {
        Program.program1.Value = 5;
    }

    static List<Program> values = new List<Program>();
    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,如果删除静态构造函数中的代码,则打印“空”。

有没有办法强制静态字段初始化,即使是否尚未使用?

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

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

编辑:上面的例子被破坏了,试试这个:

class Subclass<T> {
    static Subclass()
    {
        Values = new List<Subclass<T>>();
    }
    public Subclass()
    {
        if (!Values.Any(i => i.Value.Equals(this.Value)))
        {
            Values.Add(this);
        } 
    }

    public T Value { get; set; }

    public static List<Subclass<T>> Values { get; private set; }
}

class Superclass : Subclass<int>
{
    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();
    }
}

5 个答案:

答案 0 :(得分:7)

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

语言规范中的相关部分为10.11 Static constructors,具体为:

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

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

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

答案 1 :(得分:6)

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

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

class Subclass<TSubclass, T> 
{
    static Subclass()
    {
        Values = new List<Subclass<TSubclass, T>>();
        // 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<Subclass<TSubclass, T>> Values { get; private set; }
}

class Superclass : Subclass<Superclass, int>
{
    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)的调用会强制执行TSubclass的静态构造函数(如果它尚未运行)。这样可以确保静态字段首先根据https://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx

中的这一行进行初始化
  

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

这是一个证明它有效的dotnetfiddle:

https://dotnetfiddle.net/MfXzFd

答案 2 :(得分:4)

但是你永远不会设置属性 - 而是直接设置支持字段,因此在创建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 };

答案 3 :(得分:2)

实际上看起来拼错了'价值' - &gt; '值' 所以:

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

漂亮打印更多行

答案 4 :(得分:0)

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

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

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