English 中文(简体)
为什么在C#中静态初始化的顺序很重要?
原标题:
  • 时间:2009-03-18 21:29:06
  •  标签:

这段代码在C#中具有明确定义的不可工作行为:

class Foo
{
    static List<int> to = new List<int>( from ); // from is still null
    static IEnumerable<int> from = Something();
}

注意:我不是在询问如何修复那段代码,因为我已经知道如何做了。

这是什么样的合理化解释呢?C#已经进行运行时检查来检测静态成员的首次访问。为什么不将其扩展到每个成员,并在需要时运行它们,甚至更好的是让编译器在编译时确定其顺序呢?

顺便说一下:我认为对于非静态成员,也存在同样的问题(或几乎相同的问题)。

最佳回答

我可以想象一个程序员在初始化顺序方面依赖于其他静态类的副作用。你和我都知道依赖副作用是不好的实践,但这并不一定是非法的。

考虑像这样的东西:

class Foo
{
    static string header = Bar.GetHeader();
    static string version = Bar.GetVersion();
}

Bar.GetVersion 假设已调用 Bar.GetHeader。 如果编译器可以自由更改初始化顺序,则程序员无法保证初始化顺序。

丑陋,虽然如此,但是完全合法。如果你想象第二阶效应(即调用静态方法而这些方法本身依赖于具有副作用的类),你会发现编译器无法可靠地重新排列任何东西,就像在静态构造函数中无法(通常情况下)重新排列函数调用顺序一样。

问题回答

初始化器只是一个语法糖。当编译器编译您的类时,它会将该代码放置在.cctor中,并按照代码中布置的顺序将它们放置在其中。

它不执行任何检查,因为这没有任何意义。您仍然可能有初始化周期,因此它无论如何都不起作用。

如果您对此感兴趣,我在一段时间前就写了一篇博客。

C# 在检测类的第一次访问时进行运行时检查,但不会重新排列类中的静态初始化。

静态字段从上到下初始化,随后是从上到下的静态构造函数。要么更改字段的顺序,要么创建静态构造函数并从其中初始化字段。

参见C#规范中的变量初始化或此初始化程序文章。此外,与此相关的问题是C#中静态构造函数/初始化器的顺序。

我认为你需要使用的是静态构造函数。

就这样

class Foo
{
    static List<int> to;
    static IEnumerable<int> from;

    static Foo()
    {
        from = Something();
        to = new List<int>(from);
    }
}

关于为什么C#不在第一次访问时执行此操作,我只是认为在存在其他选择可以清楚地说明发生了什么时,没有必要使用那种复杂性。





相关问题
热门标签