私はコードの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
呼び出しをすべてスキップします。
'y'がメソッドを呼び出すとオブジェクトを廃棄する必要がある場合は、' x'がnullの場合にリークが発生します。 –
@ M.Babcock実際にはリークではなく、メモリをクリアするのを延期するだけです。そして、改訂された例を見て、私はこれが問題をより良く説明することを望んでいます。 – Aidiakapi
実際には、 'local'がnullでなくメソッドが実際に呼び出された場合、結果がGCから収集されずにリークするため、間違っていると言いました。メソッドが実際に呼び出されているかどうかの質問に答えるには、メソッドにブレークポイントを設定して実行することができます。 –