Một thời gian ngắn tôi biên soạn hai phiên bản mã, một phiên bản sử dụng (Nullable<T>)x.GetValueOrDefault(y)
và một sử dụng (Nullable<T>)x ?? y)
.Null coalesce operator affications?
Sau khi biên dịch sang IL, tôi nhận thấy rằng toán tử kết hợp rỗng được chuyển thành cuộc gọi GetValueOrDefault
.
Vì đây là phương thức mà một biểu thức có thể được truyền qua được đánh giá trước khi thực hiện phương thức, y
dường như luôn được thực thi.
Ví dụ:
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();
}
}
Dường như kết quả vào một đối tượng làm mích, và có lẽ tác động hiệu suất quá.
Trước hết, điều này có đúng không? Hoặc JIT hoặc một cái gì đó giống như thay đổi mã ASM thực sự thực hiện?
Và thứ hai ai đó có thể giải thích lý do tại sao ứng dụng này có hành vi này?
LƯU Ý: Đây chỉ là một ví dụ, nó không dựa trên mã thực, và vui lòng không đưa ra các nhận xét như 'đây là mã xấu'.
IL DASM:
Được rồi, khi tôi biên soạn này với .Net Framework 2.0 nó dẫn trong mã giống hệt với gọi liên hiệp vô GetValueOrDefault. Với .Net Framework 4.0, nó tạo ra hai mã này:
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
Null liên hiệp:
.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
Khi nó quay ra rằng đây không còn là trường hợp và bỏ qua cuộc gọi GetValueOrDefault
hoàn toàn khi HasValue
trả về sai.
Nếu 'y' là một cuộc gọi đến một phương thức dẫn đến một đối tượng cần được xử lý thì bạn sẽ bị rò rỉ trong bất kỳ trường hợp nào' x' là rỗng. –
@ M.Babcock Không thực sự là một sự rò rỉ, chỉ là một sự trì hoãn làm sạch bộ nhớ.Và nhìn vào ví dụ đã sửa đổi, tôi hy vọng điều này sẽ giải thích vấn đề tốt hơn. – Aidiakapi
Thực ra tôi đã nói sai, nếu 'local' không phải là null và phương thức này thực sự được gọi thì bạn sẽ bị rò rỉ vì kết quả sẽ được thu thập từ GC mà không bị xử lý. Để trả lời câu hỏi liệu phương thức này có thực sự được gọi mỗi lần không, bạn có thể đặt một điểm ngắt trong phương thức và chạy nó. –