1

今日、興味深いバグに遭遇しました。以下のコードは、一部のマシンではコメント行でクラッシュし、他のマシンではクラッシュしませんでした。この問題は、静的コンストラクターと静的イニシャライザーの順序付け、および継承に関連しているようです。

修正は #region のコードを別のクラスに移動することでしたが、実際に何が起こっていたのか、なぜ一部のマシンでのみ発生したように見えるのか、まだわかりません。

私はこれらの 2 つの記事を見てきました:
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。経験則では、 class の静的コンストラクターBが呼び出されると、最初にその祖先である 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 に答える