2012-01-08 11 views
8

कुछ समय पहले मैंने कोड के दो संस्करणों को संकलित किया, एक (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 झूठी रिटर्न देता है।

+0

यदि 'y' एक विधि के लिए एक कॉल है जिसके परिणामस्वरूप किसी ऑब्जेक्ट को डिस्पोजेड करने की आवश्यकता होती है तो आपके पास किसी भी मामले में एक लीक है जो' x' शून्य है। –

+0

@ एम। बाबाकॉक वास्तव में एक रिसाव नहीं है, बस स्मृति को साफ़ करने का एक स्थगित।और संशोधित उदाहरण को देखें, मुझे आशा है कि यह इस मुद्दे को बेहतर समझाएगा। – Aidiakapi

+0

असल में मैंने कहा कि गलत, यदि 'स्थानीय 'शून्य नहीं है और विधि वास्तव में बुलाया जाता है तो आपको रिसाव होगा क्योंकि परिणाम जीसी से निपटने के बिना एकत्र किया जाएगा। इस सवाल का जवाब देने के लिए कि क्या विधि वास्तव में हर बार बुलाया जा रहा है, आप विधि में ब्रेकपॉइंट डाल सकते हैं और इसे चला सकते हैं। –

उत्तर

6

आईएल को अपनाने के बाद मैंने देखा कि नल कोलेसे ऑपरेटर GetValueOrDefault कॉल में बदल गया है।

x ?? yx.HasValue ? x.GetValueOrDefault() : y में बदल दिया गया है। यह x.GetValueOrDefault(y) में परिवर्तित नहीं हुआ है, और यदि यह था तो यह एक कंपाइलर बग होगा। आप सही हैं, y का मूल्यांकन नहीं किया जाना चाहिए यदि x शून्य नहीं है, और यह नहीं है।

संपादित: y के मूल्यांकन दुष्प्रभाव से मुक्त साबित किया जा सकता है, तो x.GetValueOrDefault(y) करने के लिए एक परिवर्तन जरूरी गलत नहीं होगा (जहां "पक्ष प्रभाव" भी शामिल है "एक अपवाद फेंकता है"), लेकिन यह अभी भी एक है परिवर्तन जो मुझे नहीं लगता कि संकलक करता है: ऐसी सभी स्थितियां नहीं हैं जहां अनुकूलन उपयोगी होगा।

+0

मैंने इस परीक्षण को बहुत समय पहले नेट फ्रेमवर्क 2.0 कंपाइलर का उपयोग करके किया था, मैंने कुछ और संसाधन भी किया था, और एमएस द्वारा संदर्भ कोड की आपूर्ति से यह स्पष्ट है कि 'GetValueOrDefault' आंतरिक मान सीधे देता है, जबकि' get_Value' एक निष्पादित करता है पहले जांचें, यह सबसे अधिक संभावना है कि यह 'get_Value' के बजाय 'GetValueOrDefault' के लिए क्यों चुनता है। धन्यवाद :) – Aidiakapi

+0

हां, 'वैल्यू 'प्रॉपर्टी गेटर मूल रूप से' if (! HasValue) फेंक दिया गया है; GetValueOrDefault(); '- तो इसे कॉल करने में थोड़ा सा बिंदु है यदि आप पहले से ही निर्धारित कर चुके हैं कि नलिका ऑब्जेक्ट का मूल्य है :) – hvd

+0

या सटीक होने पर 'if (! HasValue) फेंक दें ...'; वापसी मूल्य; '। जबकि 'GetValueOrDefault() '' वापसी मान है; '। काफी दिलचस्प है कि यह (अधिकांश) structs की अपरिवर्तनीयता के कारण मान्य है, इसे केवल 'Nullable ' रैपर के आसपास रैपर को फिर से बनाना है, इसलिए 'GetValueOrDefault()' में HasValue को जांचने के लिए बहुत अधिक उपयोग नहीं है क्योंकि संरचना स्वचालित रूप से प्रारंभ होती है बाहर सभी बिट्स 0 के लिए डिफ़ॉल्ट। – Aidiakapi

संबंधित मुद्दे