【问题标题】:Is it safe for a class to create instances of itself inside the static constructor?类在静态构造函数中创建自己的实例是否安全?
【发布时间】:2012-02-20 09:46:12
【问题描述】:

我偶然发现了一个问题,我需要在其静态构造函数中创建一个类的实例。我认为这是不可能的,所以我尝试了以下方法:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Foo.someString);
        Console.ReadLine();
    }
}

class Foo
{
    public static readonly string someString;

    static Foo()
    {
        someString = new Foo().CreateString();
    }

    private string CreateString()
    {
        return "some text";
    }
}

令我惊讶的是,它有效 - 输出是“一些文本”。我相信静态构造函数必须在创建类的实例之前运行并完成。 This answer 表明情况并非如此。这是否意味着静态和实例构造函数是相互独立的?最后,这样做是否安全(在静态构造函数中创建实例)?

附言让我们忽略这样一个事实,即通过使用不同的方法可以避免这种情况。

【问题讨论】:

    标签: c#


    【解决方案1】:

    specification 所说的只是静态构造函数将在创建类的任何实例之前被调用。但它没有说明这个构造函数必须完成的事实:

    静态构造函数用于初始化任何静态数据,或 执行只需要执行一次的特定操作。它 在创建第一个实例之前自动调用 静态成员被引用。

    您可以在静态构造函数中完美地创建类的实例,这是安全的。

    【讨论】:

    • 取决于安全的定义、实例化的位置和创建的实例的用途
    • @Rune FS - 可以安全地执行与示例中类似的操作 - 初始化静态成员变量
    • 当静态构造函数依赖于其他静态构造函数时要小心。尽量不要获得循环依赖,因为这可能会导致您在静态初始化程序完成运行之前访问成员并创建类的实例。框架不会对此发出警告(即没有异常或死锁)。
    • @Ruben - 我知道循环依赖问题。但是,在这种情况下,我的情况很干净——它类似于我给出的简化示例。但是感谢警告。
    • @loodakrawa 一般无法回答,因为它依赖于 CreateString 的实现。您使用一个安全的常量。如果实现依赖于类的其他静态部分,那么在构造函数中初始化的顺序就会成为问题。如果您除了实例化并调用实例方法之外什么都不做,或者将其作为最后一件事执行,那么很可能它是安全的,但也很奇怪,它可能(故意)在未来版本的 CLR/C# 中被忽略如果它妨碍了更重要的事情
    【解决方案2】:

    它是安全的,因为它只会被调用一次。

    【讨论】:

      【解决方案3】:

      在创建第一个实例或引用任何静态成员引用任何静态字段之前自动调用静态构造函数。因此,当您的应用程序运行并访问Foo 时,静态构造函数被执行并且您的字符串被初始化。

      安全吗?:因此,这样做没有害处。只是它们只执行一次。

      有关此信息,请阅读Static Classes and Static Class Members on MSDN

      【讨论】:

      • 首先静态构造函数在第一次访问类型(成员)时调用,而不是在加载持有该类型的程序集时调用。其次,当在上面调用静态构造函数时,会创建一个实例,这就是 OP 所要求的是否安全。因此,即使关于何时调用构造函数的语句确实会改变 OP 在该特定时间实例化
      • 不知道谁是反对者,但您的陈述“当应用程序加载到内存中时调用静态构造函数。”不是真的。
      • 我没有投反对票,但我想这是因为“当应用程序加载到内存中时”这句话。这不是真的:它只是在您开始使用该类之前及时运行(每个框架版本的确切语义不同)。如果您不以任何方式使用该类,则永远不会调用静态初始化程序。有时,即使您正在使用一个类,它甚至都不会被调用(例如,当您在一个不访问该类的静态字段的类上调用一个静态方法时;同样,每个框架的确切行为也不同版本)。
      • @Ruben 我得到的以上信息来自MSDN。 msdn.microsoft.com/en-us/library/k9x6w0hc(v=vs.80).aspx.
      • 我会在 dv'ed 之后立即发布评论,这是我至少可以做的。但是,它们不会立即发布。我已经删除了dv。由于该声明已更正
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-19
      • 2013-03-05
      • 1970-01-01
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      相关资源
      最近更新 更多