6

C#でシングルトンを構築するための最良の方法を調査しているときに、C++で簡単に言及されている次の記事に出くわしました。

「C++仕様では、静的変数の初期化順序にあいまいさが残っています。」

私は結局質問を調べて、これこれを見つけました。基本的に(私が理解している限り)重要なのは、C++での静的変数の初期化順序が未定義であるということです。さて、これまでのところとても良いと思いますが、その後、記事が後で行う次のステートメントを理解したいと思いました

「幸いなことに、.NET Frameworkは、変数の初期化を処理することで、このあいまいさを解決しています。」

だから私は彼らが言うこのページを見つけました

クラスの静的フィールド変数初期化子は、クラス宣言に表示されるテキストの順序で実行される一連の割り当てに対応します。

の例を挙げてください

using System;
class Test
{
   static void Main() {
      Console.WriteLine("{0} {1}", B.Y, A.X);
   }
   public static int F(string s) {
      Console.WriteLine(s);
      return 1;
   }
}
class A
{
   static A() {}
   public static int X = Test.F("Init A");
}
class B
{
   static B() {}
   public static int Y = Test.F("Init B");
}

the output must be: 
Init B 
Init A
1 1

「静的コンストラクターの実行時のルール(セクション10.11で定義)では、Bの静的コンストラクター(したがってBの静的フィールド初期化子)は、Aの静的コンストラクターとフィールド初期化子の前に実行する必要があると規定されているためです。」

しかし、私が混乱しているのは、これらの例の静的変数の初期化順序は、クラス内のメソッドまたはフィールドが最初に呼び出されたとき、つまりコードブロックの実行順序に基づいているということです。 (この場合は左から右)。IE:クラス宣言の場所(または順序)に完全に依存しません。しかし、その記事の私の解釈によれば、それはそれらのクラスの宣言の順序の結果であると言っていますが、私のテストはバックアップしていませんか?

誰かが私のためにこれ(そして記事が作ろうとしているポイント)を明確にして、おそらく記述された振る舞いを非識字にするより良い例を提供してくれませんか?

4

2 に答える 2

8

クラスの静的フィールド変数初期化子は、クラス宣言に表示されるテキストの順序で実行される一連の割り当てに対応します。

これは、同じクラス内で、静的フィールドがソースコードに表示される順序で初期化されることを意味します。例えば:

class A
{
   public static int X = Test.F("Init A.X");
   public static int Y = Test.F("Init A.Y");
}

静的フィールドが初期化されるときは、のX前に初期化されることが保証されていますY

「静的コンストラクターの実行時のルール(セクション10.11で定義)では、Bの静的コンストラクター(したがってBの静的フィールド初期化子)は、Aの静的コンストラクターとフィールド初期化子の前に実行する必要があると規定されているためです。」

これは、これらのクラスにアクセスする式が表示されたときに、各クラスの静的コンストラクターとメンバーの初期化が評価順に実行されることを意味します¹。ソースコードでのクラス定義の相対的な出現順序は、同じソースファイルに出現する場合でも、何の役割も果たしません(最も確実にそうする義務はありません)。例えば:

static void Main() {
    Console.WriteLine("{0} {1}", B.Y, A.X);
}

A静的に初期化されていない、またはBすでに初期化されていないことを前提とすると、評価の順序により、のすべてのフィールドがのフィールドのB前に初期化されることが保証されAます。各クラスのフィールドは、最初のルールで指定された順序で初期化されます。


¹この議論の目的のために、私はの存在を無視していbeforefieldinitます。

于 2012-07-09T09:55:37.813 に答える
3

C ++では、単一の変換ユニットで静的ストレージ期間を持つ変数の初期化の順序は、そのような変数の定義が発生する順序です。静的ストレージ期間を持つ変数の初期化の順序は、さまざまな変換ユニット間でどのようになっているのかは指定されていません。

つまり、C ++標準は、引用したものと同様の保証を提供し、そのような変数を定義する単一の変換単位の定義の順序をクラスの宣言の順序に置き換えます。しかし、それは重要な違いではありません。

C ++ではこれが唯一の保証ですが、C#では、クラスを最初に使用する前にすべての静的メンバーが初期化されるという追加の保証があります。これは、プログラムが依存しているA場合(最悪の場合、異なるアセンブリ内の各タイプを考慮)、のすべての静的フィールドの初期化を開始し、次にそれらの静的初期化のいずれかに依存してAいる場合、初期化を開始することを意味します静的メンバーの数がそこでトリガーされます。ABB

静的初期化[*]中に、静的期間を持つ他のすべての変数が初期化されると想定されるC++とは対照的です。これが主な違いです。C++はそれらが初期化されていると想定し、C#はそれらがその使用前にあることを保証します。


[*]技術的にこれが問題となるのは、標準の動的初期化である可能性があります。各変換ユニット内の静的ストレージ期間を持つ変数の初期化は2段階のプロセスであり、最初のパスで静的初期化によって変数が固定定数式に設定され、その後、動的初期化と呼ばれる2番目のパスで、初期化子を持つ静的ストレージを持つすべての変数が設定されます。定数式ではありませんが初期化されます。

于 2012-07-09T11:10:33.550 に答える