繁体   English   中英

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

[英]Is there a way to force static fields to be initialized in C#?

考虑以下代码:

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,如果删除了静态构造函数中的代码,它会打印“Empty”。

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

我需要一个名为 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();
    }
}

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

语言规范中的相关部分是Static constructors ,特别是:

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

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

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

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

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

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

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

https://dotnetfiddle.net/MfXzFd

但是您永远不会设置属性——而是直接设置支持字段,因此在创建程序 1、程序 2 和程序 3 时不会通过您的逻辑添加到静态列表。

即你需要改变:

    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 };

实际上看起来你拼错了 'value' -> 'Value' 所以:

    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 这样的组合框架。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM