कुछ समय पहले मैंने कोड के दो संस्करणों को संकलित किया, एक (Nullable<T>)x.GetValueOrDefault(y)
का उपयोग करके और (Nullable<T>)x ?? y)
का उपयोग कर रहा था।नल coalesce ऑपरेटर प्रभाव?
आईएल को अपनाने के बाद मैंने देखा कि नल कोलेसे ऑपरेटर 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();
}
}
एक बीमार वस्तु में परिणाम के लिए लगता है, और शायद प्रदर्शन भी निहितार्थ।
सबसे पहले, क्या यह सच है? या क्या जेआईटी या कुछ वास्तव में वास्तव में निष्पादित एएसएम कोड बदलता है?
और दूसरी बात यह बता सकती है कि इसका व्यवहार क्यों है?
नोट: यह एक उदाहरण मात्र है, यह वास्तविक कोड पर आधारित नहीं है, और इस तरह 'यह बुरा कोड है' टिप्पणी करने से बचें।
आईएल DASM:
ठीक है, जब मैं नेट फ्रेमवर्क 2.0 के साथ इस संकलित यह शून्य सम्मिलित और 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 के साथ, यह इन दोनों कोड जनरेट करती
.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
झूठी रिटर्न देता है।
यदि 'y' एक विधि के लिए एक कॉल है जिसके परिणामस्वरूप किसी ऑब्जेक्ट को डिस्पोजेड करने की आवश्यकता होती है तो आपके पास किसी भी मामले में एक लीक है जो' x' शून्य है। –
@ एम। बाबाकॉक वास्तव में एक रिसाव नहीं है, बस स्मृति को साफ़ करने का एक स्थगित।और संशोधित उदाहरण को देखें, मुझे आशा है कि यह इस मुद्दे को बेहतर समझाएगा। – Aidiakapi
असल में मैंने कहा कि गलत, यदि 'स्थानीय 'शून्य नहीं है और विधि वास्तव में बुलाया जाता है तो आपको रिसाव होगा क्योंकि परिणाम जीसी से निपटने के बिना एकत्र किया जाएगा। इस सवाल का जवाब देने के लिए कि क्या विधि वास्तव में हर बार बुलाया जा रहा है, आप विधि में ब्रेकपॉइंट डाल सकते हैं और इसे चला सकते हैं। –