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

Raf*_*mão 7 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它仅打印数字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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*tte 8

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

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

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

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

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


Tyr*_*son 7

在这种情况下,实际上有一种方法可以强制初始化属性.此更改需要向基类添加一个类型参数,以表示将包含要初始化的字段的基类的未来子类.然后我们可以使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

https://dotnetfiddle.net/MfXzFd

  • 是的,但问题是 SuperClassCustom 确实提供了新功能、新属性和东西。当我们想要定制一些东西时,我们不能触及“核心”文件。这才是真正的问题。不过找到了一个可行的解决方案。不太好,但它可以完成工作。我将遍历所有程序集和类型一次以调用所有静态构造函数(如果它们派生自 TSubclass):https://dotnetfiddle.net/C0BYCM (2认同)

Row*_*haw 5

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

即你需要改变:

    static Program program1 = new Program { value = 1 };
    static Program program2 = new Program { value = 2 };
    static Program program3 = new Program { value = 3 };
Run Code Online (Sandbox Code Playgroud)

到:

    static Program program1 = new Program { Value = 1 };
    static Program program2 = new Program { Value = 2 };
    static Program program3 = new Program { Value = 3 };
Run Code Online (Sandbox Code Playgroud)

  • @ Travis “这就是为什么我们不为成员和属性名称使用相同的变量名称!” - 实际上,您可以使用相同的名称,dit 是 C# 中非常常见的样式,但您通常会将支持字段设置为私有,并且仅将属性公开为公共。 (2认同)