1

我今天遇到了一个有趣的错误,下面的代码会在某些机器上的注释行上崩溃,而在其他机器上则不会。该问题似乎与静态构造函数、静态初始化程序和继承的顺序有关。

解决方法是将#region 中的代码移到另一个类中,但我仍然不明白实际发生了什么,以及为什么它似乎只发生在某些机器上。

我看过这两篇文章:http: //csharpindepth.com/Articles/General/Singleton.aspx
http://csharpindepth.com/Articles/General/BeforeFieldInit.aspx

这揭示了一些见解,但都没有涉及继承如何影响事物。

public class CountAggregator : Aggregator
{
    private static readonly CountAggregator _instance = new CountAggregator();

    public static CountAggregator Instance
    {
        get
        {
            return _instance; 
        }
    }

    private CountAggregator() : base("CNT")
    {
    }

}



public class Aggregator
{

    protected Aggregator(string id)
    {
        Id = id;
    }

    public string Id { get; private set; }





    #region All Aggregators

    private static readonly List<Aggregator> _allAggregators = new List<Aggregator>();
    private static readonly Dictionary<string, Aggregator> _aggregatorsById = new Dictionary<string, Aggregator>();

    public static IEnumerable<Aggregator> All
    {
        get { return _allAggregators; }
    }

    public static Aggregator GetAggregator(string id)
    {
        return _aggregatorsById[id];
    }

    static Aggregator()
    {
        _allAggregators.AddRange(new Aggregator[]
        {
             CountAggregator.Instance,
        }

        foreach (var aggregator in _allAggregators)
        {
            //this prints false, and the next line crashes
            HtmlPage.Window.Alert((aggregator != null).ToString());
            _aggregatorsById.Add(aggregator.Id, aggregator);                
        }        
    }

    #endregion 

}
4

1 回答 1

2

让我们有类B,继承类A。经验法则是,当B调用 class 的静态构造函数时,首先要确保其祖先 classA之前已初始化。然而,当A' 的静态构造函数首先被初始化时,依赖于它的祖先B(这很奇怪),B' 的静态构造函数不能在 ' 完成之前执行A,这导致任何B' 的字段都处于默认状态(=零,空)状态。

当您第一次访问B代码中的任何位置时,顺序如下:

Access B
    Invoke B's static constructor
        Invoke A's static constructor, if necessary
            initialize A's static fields
            execute the constructor's code
        initialize B's static fields
        execute the constructor's code

另一方面,当您第一次访问A代码中的任何位置时,顺序如下:

Access A
    Invoke A's static constructor
        initialize A's static fields
        execute the constructor's code, which includes
            Access B and its static field B.Field
                Invoke B's static constructor — circular dependency on A, cannot call
                return B.Field which is uninitialized i.e. zero / null
            use invalid value of B.Field

您提取该区域中包含的代码的解决方案非常合乎逻辑,并且由于关注点分离,您无论如何都应该这样做。

于 2014-01-08T18:05:19.850 回答