2012-01-08 11 views
8

私はコードの2つのバージョンをコンパイルしました.1つは(Nullable<T>)x.GetValueOrDefault(y)を使用し、もう一方は(Nullable<T>)x ?? y)を使用しました。Nullは演算子の意味を統合しますか?

ILに逆コンパイルした後、NULL合体演算子がGetValueOrDefault呼び出しに変換されていることがわかりました。

メソッドの実行前に評価される式を渡すことができるメソッド呼び出しであるため、yは常に実行されているようです。例えば

using System; 

public static class TestClass 
{ 
    private class SomeDisposable : IDisposable 
    { 
     public SomeDisposable() 
     { 
      // Allocate some native resources 
     } 

     private void finalize() 
     { 
      // Free those resources 
     } 

     ~SomeDisposable() 
     { 
      finalize(); 
     } 

     public void Dispose() 
     { 
      finalize(); 
      GC.SuppressFinalize(this); 
     } 
    } 

    private struct TestStruct 
    { 
     public readonly SomeDisposable _someDisposable; 
     private readonly int _weirdNumber; 

     public TestStruct(int weirdNumber) 
     { 
      _weirdNumber = weirdNumber; 
      _someDisposable = new SomeDisposable(); 
     } 
    } 

    public static void Main() 
    { 
     TestStruct? local = new TestStruct(0); 

     TestStruct local2 = local ?? new TestStruct(1); 

     local2._someDisposable.Dispose(); 
    } 
} 

はindisposedオブジェクトになるようだ、とあまりにもおそらくパフォーマンスへの影響。

まず、これは本当ですか?または、JITなどが実際に実行されるASMコードを変更しますか?

第2に、なぜこの動作があるのか​​説明できますか?

注:これは単なる例であり、実際のコードに基づいていないため、「これは悪いコード」のようなコメントを控えてください。

IL DASM:
さて、私は、.NET Frameworkの2.0でこれをコンパイルし、それがnull合体とGetValueOrDefaultを呼び出すと、同一のコードが生じました。

GetValueOrDefault:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  19 (0x13) 
    .maxstack 2 
    .locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> nullableInt, 
      [1] int32 nonNullableInt) 
    IL_0000: nop 
    IL_0001: ldloca.s nullableInt 
    IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32> 
    IL_0009: ldloca.s nullableInt 
    IL_000b: ldc.i4.1 
    IL_000c: call  instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault(!0) 
    IL_0011: stloc.1 
    IL_0012: ret 
} // end of method Program::Main 

ヌル合体:.NET Frameworkの4.0では、それはこれらの2つのコードを生成し、これはもはやケースであることが判明していないとして

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  32 (0x20) 
    .maxstack 2 
    .locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0, 
      int32 V_1, 
      valuetype [mscorlib]System.Nullable`1<int32> V_2) 
    IL_0000: nop 
    IL_0001: ldloca.s V_0 
    IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32> 
    IL_0009: ldloc.0 
    IL_000a: stloc.2 
    IL_000b: ldloca.s V_2 
    IL_000d: call  instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue() 
    IL_0012: brtrue.s IL_0017 
    IL_0014: ldc.i4.1 
    IL_0015: br.s  IL_001e 
    IL_0017: ldloca.s V_2 
    IL_0019: call  instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault() 
    IL_001e: stloc.1 
    IL_001f: ret 
} // end of method Program::Main 

HasValueがfalseを返すと、GetValueOrDefault呼び出しをすべてスキップします。

+0

'y'がメソッドを呼び出すとオブジェクトを廃棄する必要がある場合は、' x'がnullの場合にリークが発生します。 –

+0

@ M.Babcock実際にはリークではなく、メモリをクリアするのを延期するだけです。そして、改訂された例を見て、私はこれが問題をより良く説明することを望んでいます。 – Aidiakapi

+0

実際には、 'local'がnullでなくメソッドが実際に呼び出された場合、結果がGCから収集されずにリークするため、間違っていると言いました。メソッドが実際に呼び出されているかどうかの質問に答えるには、メソッドにブレークポイントを設定して実行することができます。 –

答えて

6

ILに逆コンパイルした後、NULL合体演算子がGetValueOrDefault呼び出しに変換されていることがわかりました。

x ?? yは、x.HasValue ? x.GetValueOrDefault() : yに変換される。 x.GetValueOrDefault(y)に変換されず、コンパイラのバグになります。あなたは正しい、yは、xがヌルでない場合、評価されるべきではありません。

編集は:yの評価は副作用のない証明できる場合は、x.GetValueOrDefault(y)への変換は必ずしも間違っているではありません(「副作用」には、「例外をスロー」)が、それはまだです私はコンパイラが実行するとは思わない変換:その最適化が有用である多くの状況すべてがあるわけではありません。

+0

私は.NET Framework 2.0コンパイラを使用してこのテストを行いました。私はもう少しリソースを使いました。また、MSによる参照コードの提供から、 'GetValueOrDefault'は内部値を直接返します。最初にチェックすると、 'get_Value'ではなく' GetValueOrDefault'を選択する可能性が高くなります。ありがとうございました:) – Aidiakapi

+0

はい、 'Value'プロパティゲッターは基本的に' if(!HasValue)throw;戻り値GetValueOrDefault(); ' - ヌル可能オブジェクトに値があるかどうかをすでに判断している場合は、それを呼び出すのに少しのポイントがあります:) – hvd

+0

正確に' if(!HasValue)throw' ... ';戻り値; '。 'GetValueOrDefault()'は 'return value;'です。構造体の不変性のために有効であることは非常に興味深いことですが、 'Nullable 'ラッパーを 'T'の周りに再作成するだけで済むので、GetValueOrDefault()のHasValueのチェックにはあまり役に立ちません。 outはすべてのビット0にデフォルト設定されています。 – Aidiakapi

関連する問題