9

私は C# を深く掘り下げて、null 許容値型で遊んでいます。実験的な目的で、私はコードを書きました:

    private static void HowNullableWorks()
    {
        int test = 3;
        int? implicitConversion = test;
        Nullable<int> test2 = new Nullable<int>(3);

        MethodThatTakesNullableInt(null);
        MethodThatTakesNullableInt(39);
    }

そして、implicitConversion / test2変数が次のように初期化されていることに驚きました。

call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

一方、MethodThatTakesNullableIntが呼び出されると、次のことがわかります。

IL_0017:  initobj    valuetype [mscorlib]System.Nullable`1<int32>

IL_0026:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)

私は理解しています。ImplicitConversion / test2のnewobj命令も表示されると思いました。

これは完全な IL コードです。

.method private hidebysig static void  HowNullableWorks() cil managed
{
  // Code size       50 (0x32)
  .maxstack  2
  .locals init ([0] int32 test,
           [1] valuetype [mscorlib]System.Nullable`1<int32> implicitConversion,
           [2] valuetype [mscorlib]System.Nullable`1<int32> test2,
           [3] valuetype [mscorlib]System.Nullable`1<int32> CS$0$0000)
  IL_0000:  nop
  IL_0001:  ldc.i4.3
  IL_0002:  stloc.0
  IL_0003:  ldloca.s   implicitConversion
  IL_0005:  ldloc.0
  IL_0006:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_000b:  nop
  IL_000c:  ldloca.s   test2
  IL_000e:  ldc.i4.3
  IL_000f:  call       instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_0014:  nop
  IL_0015:  ldloca.s   CS$0$0000
  IL_0017:  initobj    valuetype [mscorlib]System.Nullable`1<int32>
  IL_001d:  ldloc.3
  IL_001e:  call       void csharp.in.depth._2nd.Program::MethodThatTakesNullableInt(valuetype [mscorlib]System.Nullable`1<int32>)
  IL_0023:  nop
  IL_0024:  ldc.i4.s   39
  IL_0026:  newobj     instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
  IL_002b:  call       void csharp.in.depth._2nd.Program::MethodThatTakesNullableInt(valuetype [mscorlib]System.Nullable`1<int32>)
  IL_0030:  nop
  IL_0031:  ret
} // end of method Program::HowNullableWorks
4

1 に答える 1

3

まず、(nopsに基づいて)デバッグモードでコンパイルしたように見えます。リリースモードでコンパイルすると、異なるコードが出力される可能性があります。

ECMA CLR仕様のセクションI.12.1.6.2.1(値型のインスタンスの初期化)は次のように述べています。

値型インスタンスのホームを初期化するには、3つのオプションがあります。ホームのアドレスをロードし(表I.8:ホームロケーションのアドレスとタイプを参照)、initobj 命令を使用してゼロにすることができます(ローカル変数の場合、これはlocalsinitメソッドのヘッダーのビットを設定することによっても実行されます)。ユーザー定義のコンストラクターを呼び出すには、ホームのアドレスをロードし(表I.8:ホームの場所のアドレスとタイプを参照)、コンストラクターを直接呼び出します。または、§I.12.1.6.2.2で説明されているように、既存のインスタンスをホームにコピーできます。

コードでnull許容型を最初に3回使用すると、ローカルにnull値が格納されるため、このコメントは適切です(ローカルは、値のホームの1つのタイプです)。最初の2つはローカルimplicitConversionでありtest、宣言したものであり、3番目はローカルです。は、コンパイラによって生成された一時的なと呼ばれCS$0$0000ます。ECMA仕様が示すように、これらのローカルは、を使用してinitobj(構造体のデフォルトの引数なしコンストラクターと同等でありCS$0$0000、この場合に使用されます)、またはローカルのアドレスをロードしてコンストラクターを呼び出すことによって初期化できます(他の2人の地元住民)。

ただし、最終的なnull許容インスタンス(からの暗黙的な変換によって作成された39)の場合、結果はローカルに格納されません。スタックで生成されるため、ホームを初期化するためのルールはここでは適用されません。代わりに、コンパイラnewobjはスタック上に値を作成するために使用します(他の値や参照型の場合と同様)。

コンパイラがへの呼び出しに対してローカルを生成したが、に対しては生成しなかったのはなぜか疑問に思われるかもしれMethodThatTakesNullableInt(null)ませんMethodThatTakesNullableInt(39)。答えは、コンパイラが常にinitobjデフォルトのコンストラクタを呼び出すために使用することだと思います(値にはローカルまたは他のホームが必要です)がnewobj、適切なホームがまだない場合は、他のコンストラクタを呼び出して結果をスタックに格納するために使用します値について。

詳細については、仕様のセクションIII.4.21(newobj)の次のコメントも参照してください。

値型は通常、を使用して作成されませんnewobj。これらは通常、引数またはローカル変数として、newarr(ゼロベースの1次元配列の場合)を使用して、またはオブジェクトのフィールドとして割り当てられます。割り当てられると、を使用して初期化されますinitobj。ただし、このnewobj 命令を使用して、スタック上に値型の新しいインスタンスを作成し、それを引数として渡したり、ローカルに格納したりすることができます。

于 2012-08-15T16:22:09.127 に答える