5

型に静的コンストラクターがない場合、フィールド初期化子は、型が使用される直前、またはランタイムの気まぐれでいつでも実行されます。

なぜこのコード:

void Main()
{ 
  "-------start-------".Dump();
   Test.EchoAndReturn("Hello");
  "-------end-------".Dump();

}

 class Test
{
    public static string x = EchoAndReturn ("a");
    public static string y = EchoAndReturn ("b");
    public static string EchoAndReturn (string s)
    {
        Console.WriteLine (s);
        return s;
    }
}

利回り :

-------start-------
a
b
Hello
-------end-------

このコードの間:

void Main()
{ 
  "-------start-------".Dump();
   var test=Test.x;
  "-------end-------".Dump();

}

収量

a
b
-------start-------
-------end-------

aとの順番bが分かります。しかし、なぜ対処は と static method異なります static field

静的メソッドと静的フィールドで開始行と終了行が異なる場所にあるのはなぜですか? つまり、どちらの状況でも、彼はそれらのフィールドを初期化する必要があります...では、なぜですか?

(静的ctorを追加して同じにすることができることは知っていますが、この特定の状況について質問しています。)

(ps Dump() は console.write と同じです)

4

3 に答える 3

6

リリース JIT の動作は (4.0 IIRC 以降)、呼び出しているメソッドが静的フィールドに触れない限り、静的初期化子を実行しないことです。これは、静的フィールド初期化されていないことを意味する場合があります。デバッガーの外部でリリースの最初のコードを実行すると、次のようになります。

-------start-------
Hello
-------end-------

デバッガーをアタッチして (リリース)、またはデバッグ ビルドで (デバッガーをアタッチして、またはアタッチせずに) 実行すると、次のようになります。

-------start-------
a
b
Hello
-------end-------

これまでのところとても興味深いです。なぜ得られるのか:

a
b
-------start-------
-------end-------

このシナリオでは、メソッドごとの JIT が本質的に静的コンストラクターの実行を担当しているようです。これは、次を追加することで確認できます。

if(NeverTrue()) { // method that returns false
        "-------start-------".Dump();
        var test = Test.x;
        "-------end-------".Dump();
}

これは出力されます(デバッガーなしのリリースでも)

a
b

そのため、フィールドにアクセスできることが重要ですフィールドにアクセスしないTest.xメソッドへの呼び出しに変更した場合(そしてそれを削除した場合)、何も出力されませんNeverTrue()

そのため、CLI の一部のバージョンでは、静的初期化子の実行が、任意のフィールドへの言及を含むメソッドの JIT ステップに延期される場合があります (そのフィールドに初期化子があるかどうかはチェックされません)。

静的フィールドに触れない限り、静的初期化子を実行せずにオブジェクトのインスタンスを作成することもできます。

 public Test()
 {
     a = "";
 }
 string a;

と:

"-------start-------".Dump();
new Test();
"-------end-------".Dump();

印刷するだけです(リリース、デバッガーなし):

-------start-------
-------end-------

でも!このタイミングに依存するものはビルドしないでください。

  • .NET バージョン間で変更されます
  • プラットフォーム (x86、x64、CF、SL、.NETCore など) によって異なる可能性があります。
  • デバッガーがアタッチされているかどうか、およびデバッグ/リリース ビルドであるかどうかによって変わる可能性があります。
于 2012-10-10T10:08:43.283 に答える
3

静的コンストラクターが呼び出される時間は保証されていないため、プログラムにとっては C++ の未定義の動作のようなものです。静的コンストラクター呼び出しのシーケンスに依存するべきではありません。たとえば、リリース時にプログラムをコンパイルすると、両方のケースで静的コンストラクターが同時に呼び出されることがわかります。

于 2012-10-10T10:09:21.790 に答える
1

これは.NET 4.0

型に静的コンストラクターがなく、初期化のある静的フィールドがある場合、コンパイラーは型コンストラクターを作成し、その中に初期化を入れます。

class Test
    {
        public static string x = EchoAndReturn("a");
        public static string y = EchoAndReturn("b");
        public static string EchoAndReturn(string s)
        {
            Console.WriteLine(s);
            return s;
        }
    }

次のILの結果(cctor部分のみ)

.method private hidebysig specialname rtspecialname static 
        void  .cctor() cil managed
{
  // Code size       31 (0x1f)
  .maxstack  8
  IL_0000:  ldstr      "a"
  IL_0005:  call       string ConsoleApplication1.Test::EchoAndReturn(string)
  IL_000a:  stsfld     string ConsoleApplication1.Test::x
  IL_000f:  ldstr      "b"
  IL_0014:  call       string ConsoleApplication1.Test::EchoAndReturn(string)
  IL_0019:  stsfld     string ConsoleApplication1.Test::y
  IL_001e:  ret
} // end of method Test::.cctor

また、 によるとCLR via C#、JIT コンパイラは各メソッドを事前にチェックし、どの型に静的コンストラクターがあるかを調べます。静的コンストラクターが呼び出されていない場合は、JIT コンパイラーが呼び出します。

これにより、2 つのコード フラグメントの違いが説明されます。

When the just-in-time (JIT) compiler is compiling a method,

コードでどの型が参照されているかを確認します。型のいずれかが型コンストラクターを定義している場合、JIT コンパイラーは型の型コンストラクターがこの AppDomain に対して既に実行されているかどうかを確認します。コンストラクターが実行されていない場合、JIT コンパイラーは呼び出しを発行します。型コンストラクターに、JIT コンパイラーが発行しているネイティブ コードに変換します。

@コメント

1 つのフィールド初期化子をユーザー定義の型コンストラクターに移動すると、コンパイラは、クラス レベルで初期化した他のフィールドも型コンストラクターに移動します。

static Test()
{
  y = EchoAndReturn("b");
}

上記と同じ IL になります。したがって、基本的に、独自の型コンストラクターとコンパイラーによって生成された型コンストラクターの間に違いはありません (とにかく 1 つしか存在できません)。

于 2012-10-10T10:32:22.230 に答える