2012-05-16 10 views
33

मैंने हमेशा सोचा है कि this के लिए उदाहरण विधि शरीर के अंदर शून्य होना असंभव है। सरल कार्यक्रम के बाद यह दर्शाता है कि यह संभव है। क्या यह कुछ दस्तावेज व्यवहार है?यह == .NET उदाहरण विधि के अंदर शून्य - यह क्यों संभव है?

class Foo 
{ 
    public void Bar() 
    { 
     Debug.Assert(this == null); 
    } 
} 

public static void Test() 
{    
    var action = (Action)Delegate.CreateDelegate(typeof (Action), null, typeof(Foo).GetMethod("Bar")); 
    action(); 
} 

अद्यतन

मैं जवाब यह कहते हुए कि यह कैसे इस विधि से प्रलेखित है कि के साथ सहमत हैं। हालांकि, मैं वास्तव में इस व्यवहार को समझ नहीं पा रहा हूं। विशेष रूप से क्योंकि यह नहीं है कि सी # कैसे डिज़ाइन किया गया है।

हम किसी से एक रिपोर्ट हो गया था (संभावना नेट समूहों सी # का उपयोग कर में से एक (सोचा था कि यह अभी तक उस समय) पर नामित नहीं किया गया था सी #) जो लिखे कोड एक अशक्त पर एक विधि कहा जाता था कि सूचक, लेकिन को अपवाद नहीं मिला क्योंकि विधि किसी भी फ़ील्ड तक पहुंच नहीं पाई थी (यानी "यह" शून्य था, लेकिन विधि में इसका उपयोग नहीं किया गया था)। उस विधि को ने एक और विधि कहा जिसने इस बिंदु का उपयोग किया और अपवाद फेंक दिया, और कुछ सिर-स्क्रैचिंग शुरू हुई। उन्हें बाहर निकालने के बाद, उन्होंने हमें इसके बारे में एक नोट भेजा। हमने सोचा था कि एक शून्य उदाहरण पर एक विधि को कॉल करने में सक्षम होने पर थोड़ा अजीब था। पीटर गोल्डे ने यह देखने के लिए कुछ परीक्षण किया कि पेर्फ प्रभाव हमेशा कॉलवर्ट का उपयोग कर रहा था, और यह इतना छोटा था कि हमने परिवर्तन करने के लिए का निर्णय लिया।

http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

+0

संकेत के लिए मेरा उत्तर देखें कि सीएलआर जानबूझकर .NET 1.0 में इस तरह से डिज़ाइन किया गया था।आपके द्वारा उद्धृत आलेख एक अलग (गैर-प्रतिनिधि) स्थिति के बारे में है, जो एक कंपाइलर ऑप्टिमाइज़ेशन होगा - उदाहरण के प्रकार को स्थिर रूप से निर्धारित किया जा सकता है जब एक प्रत्यक्ष कॉल द्वारा 'कॉलवर्ट' को बदलना। ध्यान दें कि सीएलआर को 'कॉलवर्ट' के दौरान 'NullReferenceException' को फेंकने का कारण 'इस संदर्भ' के आधार पर एक वीएमटी लुकअप की आवश्यकता है; संदर्भ की जांच करने के लिए सिर्फ एक अर्थपूर्ण इच्छा नहीं है। –

+0

संबंधित: http://stackoverflow.com/q/3143498/158779 –

+0

आईएमएचओ, यह दुर्भाग्यपूर्ण है कि निर्दिष्ट करने के लिए कोई मानक तरीका नहीं है (शायद गुणों के माध्यम से) यह निर्दिष्ट करने के लिए कि 'कॉलवर्ट' की बजाय' कॉल 'के साथ एक विधि लागू की जानी चाहिए जो उन प्रकारों को अनुमति देता है जो मूल्यों को समाहित करते हैं [जैसे 'स्ट्रिंग'] उन सदस्यों के लिए जो डिफ़ॉल्ट-मूल्यवान स्टोरेज स्थानों पर काम कर सकते हैं। 'अगर (someString.IsNullOrEmpty)' कहें तो 'if (String.IsNullOrEmpty (someString))' से अधिक क्लीनर IMHO होगा। – supercat

उत्तर

23

क्योंकि आप Delegate.CreateDelegate

की firstArgument में null गुजर रहे हैं तो आप एक अशक्त वस्तु पर एक उदाहरण विधि कॉल कर रहे हैं।

http://msdn.microsoft.com/en-us/library/74x8f551.aspx

तो firstArgument एक अशक्त संदर्भ है और विधि एक उदाहरण विधि है, परिणाम प्रतिनिधि प्रकार प्रकार के हस्ताक्षर पर और विधि की निर्भर करता है:

हैं प्रकार के हस्ताक्षर स्पष्ट रूप से छुपा पहला विधि के पैरामीटर को शामिल करता है, प्रतिनिधि को खुली इंस्टेंस विधि का प्रतिनिधित्व करने के लिए कहा जाता है।जब प्रतिनिधि को बुलाया जाता है, तो में पहला तर्क तर्क सूची विधि के छिपे हुए उदाहरण पैरामीटर को पास की जाती है।

यदि विधि और प्रकार मिलान के हस्ताक्षर (यानी, सभी पैरामीटर प्रकार संगत हैं), तो प्रतिनिधि को शून्य संदर्भ पर बंद माना जाता है। प्रतिनिधि को आमंत्रित करना एक उदाहरण उदाहरण पर विधि को कॉल करने जैसा है, जो करने के लिए विशेष रूप से उपयोगी चीज़ नहीं है।

12

ज़रूर यदि आप कॉल आईएल अनुदेश या प्रतिनिधि दृष्टिकोण का उपयोग कर रहे हैं यदि आप एक विधि में कॉल कर सकते हैं। यदि आप सदस्य फ़ील्ड तक पहुंचने का प्रयास करते हैं तो आप केवल इस बूबी जाल को सेट कर देंगे जो आपको NullReferenceException देगा जो आपने खोजा था।

कोशिश

int x; 
public void Bar() 
{ 
     x = 1; // NullRefException 
     Debug.Assert(this == null); 
} 

बीसीएल भी स्पष्ट इस == बातिल चेकों भाषाओं जो (सी #) की तरह हर समय callvirt का उपयोग नहीं करते के लिए डिबगिंग सहायता करने के लिए शामिल है। आगे के इंफोस के लिए यह question देखें।

उदाहरण के लिए स्ट्रिंग क्लास में ऐसे चेक हैं। उनके बारे में कुछ भी रहस्यमय नहीं है सिवाय इसके कि आपको सी # जैसी भाषाओं में उनकी आवश्यकता नहीं दिखाई देगी।

// Determines whether two strings match. 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
public override bool Equals(Object obj) 
{ 
    //this is necessary to guard against reverse-pinvokes and 
    //other callers who do not use the callvirt instruction 
    if (this == null) 
     throw new NullReferenceException(); 

    String str = obj as String; 
    if (str == null) 
     return false; 

    if (Object.ReferenceEquals(this, obj)) 
     return true; 

    return EqualsHelper(this, str); 
} 
5

Delegate.CreateDelegate()at msdn के लिए प्रलेखन का प्रयास करें।

आप सब कुछ "मैन्युअल रूप से" कॉल कर रहे हैं, और इस प्रकार this पॉइंटर के लिए एक उदाहरण पास करने के बजाय, आप शून्य से गुज़र रहे हैं। तो ऐसा हो सकता है, लेकिन आपको वास्तव में वास्तव में कड़ी मेहनत करनी है।

+0

वास्तव में वास्तव में आसान तरीका के लिए निम्न उदाहरण देखें। http://bradwilson.typepad.com/blog/2008/01/c-30-extension.html –

+0

विस्तार विधियां क्लास विधियों की तरह "दिखती" हो सकती हैं, लेकिन वे वास्तव में नहीं हैं। वे केवल स्थैतिक विधियां हैं जो क्लास इंस्टेंस पर कार्य करती हैं, जिनमें कुछ अतिरिक्त आसानी से उपयोग किया जाता है। तो मैं वही बात नहीं कहूंगा। –

+0

मैं आपसे सहमत हूं, लेकिन सूची विस्तार विधियों के साथ नहीं रुकती है। सी ++/सीएलआई सीधे गैर-वर्चुअल सी # विधियों को कॉल करता है, जो आपको विस्तार विधियों के उदाहरण के रूप में बिल्कुल चिकनी स्रोत कोड देता है, इस बार परिणामस्वरूप यह # = # null' सी # पक्ष पर होता है। –

5

this एक संदर्भ है, इसलिए टाइप सिस्टम के परिप्रेक्ष्य से null होने में कोई समस्या नहीं है।

आप पूछ सकते हैं कि NullReferenceException क्यों नहीं फेंक दिया गया था। परिस्थितियों की पूरी सूची जब सीएलआर उस अपवाद को फेंकता है documented है। आपका मामला सूचीबद्ध नहीं है। हां, यह callvirt है, लेकिन Delegate.Invoke (see here) Bar के बजाय this संदर्भ वास्तव में आपका गैर-शून्य प्रतिनिधि है!

आपके द्वारा देखे जाने वाले व्यवहार में सीएलआर के लिए एक दिलचस्प कार्यान्वयन परिणाम है। एक प्रतिनिधि के पास Target संपत्ति होती है (आपके this संदर्भ से मेल खाती है) जो अक्सर null होती है, अर्थात् जब प्रतिनिधि स्थिर होता है (कल्पना Bar स्थैतिक हो)। अब, स्वाभाविक रूप से, संपत्ति के लिए एक निजी समर्थन क्षेत्र है, जिसे _target कहा जाता है। _target में एक स्थिर प्रतिनिधि के लिए शून्य है? No it doesn't. इसमें प्रतिनिधि के लिए एक संदर्भ शामिल है। क्यों नहीं शून्य? चूंकि एक नल एक प्रतिनिधि का वैध लक्ष्य है क्योंकि आपका उदाहरण दिखाता है और सीएलआर में किसी भी तरह के स्थिर प्रतिनिधि को अलग करने के लिए null पॉइंटर के दो स्वाद नहीं होते हैं।

यह छोटा सा त्रिभुज दर्शाता है कि प्रतिनिधियों के साथ, उदाहरण विधियों के शून्य लक्ष्य कोई विचार नहीं हैं। आप अभी भी अंतिम सवाल पूछ सकते हैं: लेकिन उन्हें क्यों समर्थन देना पड़ा?

प्रारंभिक सीएलआर के पास दूसरों के बीच बनने की महत्वाकांक्षी योजना थी, सी ++ डेवलपर्स के लिए भी पसंद का मंच, एक लक्ष्य जिसे पहले प्रबंधित सी ++ और फिर सी ++/सीएलआई के साथ संपर्क किया गया था। कुछ चुनौतीपूर्ण भाषा सुविधाओं को छोड़ दिया गया था, लेकिन उदाहरण के बिना निष्पादित उदाहरण विधियों को समर्थन देने के बारे में वास्तव में कुछ भी चुनौतीपूर्ण नहीं था, जो सी ++ में पूरी तरह से सामान्य है। प्रतिनिधि समर्थन सहित।

इसलिए अंतिम उत्तर यह है: क्योंकि सी # और सीएलआर दो अलग-अलग दुनिया हैं।

More good reading और even better reading नल इंस्टेंस की अनुमति देने वाले डिज़ाइन को दिखाने के लिए बहुत ही प्राकृतिक सी # सिंटेक्टिक संदर्भों में भी इसका निशान दिखाता है।

0

यह सी # कक्षाओं में एक पठनीय संदर्भ है। तदनुसार और उम्मीद के अनुसार इसका उपयोग किसी अन्य संदर्भ (केवल पढ़ने के मोड में) के रूप में किया जा सकता है ...

this == null // readonly - possible 
this = new this() // write - not possible 
संबंधित मुद्दे