C # में शून्य न होने पर मेथड कॉल


106

क्या किसी तरह इस बयान को छोटा करना संभव है?

if (obj != null)
    obj.SomeMethod();

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

और घटनाओं के साथ इसी तरह की समस्या, जहां

public event Func<string> MyEvent;

और फिर आह्वान करें

if (MyEvent != null)
    MyEvent.Invoke();

41
हमने C # 4 में एक नए ऑपरेटर को जोड़ने पर विचार किया: "obj.?SomeMethod ()" का अर्थ होगा "SomeMethod को कॉल करें यदि obj अशक्त नहीं है, अन्यथा, वापसी null"। दुर्भाग्य से यह हमारे बजट में फिट नहीं हुआ इसलिए हमने इसे कभी लागू नहीं किया।
एरिक लिपर्ट

@ नोट: क्या यह टिप्पणी अभी भी मान्य है? मैंने कहीं देखा है कि, यह 4.0 के साथ उपलब्ध है?
चरिथज

@CharithJ: नहींं। इसे कभी लागू नहीं किया गया था।
एरिक लिपर्ट

3
@CharithJ: मैं अशक्त सहकर्मी ऑपरेटर की मौजूदगी से अवगत हूं। यह वह नहीं करता जो डार्थ चाहता है; वह एक लिफ्ट-टू-न्यूलेबल मेंबर एक्सेस ऑपरेटर चाहता है। : (।। और वैसे, अपने पिछले टिप्पणी अशक्त वालों ऑपरेटर का गलत लक्षण वर्णन आप कहते हैं "? M.Value लिखा जा सकता है वी = मीटर ?? y वी = मीटर == अशक्त y" का मतलब है देता है)
एरिक Lippert

4
नए पाठकों के लिए: C # 6.0 लागू होता है ?,, तो x?
डेविड

जवाबों:


162

C # 6 के बाद से, आप इसका उपयोग कर सकते हैं:

MyEvent?.Invoke();

या:

obj?.SomeMethod();

?.अशक्त-प्रचार ऑपरेटर है, और कारण होगा .Invoke()शॉर्ट सर्किट होने के लिए जब संकार्य है null। ऑपरेंड केवल एक बार एक्सेस किया जाता है, इसलिए "चेक और इनवोक के बीच मूल्य परिवर्तन" समस्या का कोई जोखिम नहीं है।

===

C # 6 से पहले, नहीं: एक अपवाद के साथ कोई शून्य-सुरक्षित जादू नहीं है; विस्तार के तरीके - उदाहरण के लिए:

public static void SafeInvoke(this Action action) {
    if(action != null) action();
}

अब यह मान्य है:

Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"

घटनाओं के मामले में, यह दौड़-स्थिति को हटाने का भी लाभ है, अर्थात आपको एक अस्थायी चर की आवश्यकता नहीं है। तो आम तौर पर आप की आवश्यकता होगी:

var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);

लेकिन इसके साथ:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if(handler != null) handler(sender, EventArgs.Empty);
}

हम बस का उपयोग कर सकते हैं:

SomeEvent.SafeInvoke(this); // no race condition, no null risk

1
'इस बारे में थोड़ा संघर्ष किया। एक्शन या इवेंट हैंडलर के मामले में - जो आमतौर पर अधिक स्वतंत्र होता है - इससे कुछ समझ में आता है। मैं एक नियमित विधि के लिए इनकैप्सुलेशन को नहीं तोड़ूंगा, हालांकि। इसका मतलब यह है कि एक अलग, स्थिर वर्ग में विधि का निर्माण और मुझे नहीं लगता कि इनकैप्सुलेशन और अपमानजनक पठनीयता / कोड संगठन को खोना स्थानीय पठनीयता में मामूली सुधार के लायक है
tvanfosson

@tvanfosson - वास्तव में; लेकिन मेरा कहना है कि यह एकमात्र मामला है जो मुझे पता है कि यह कहां काम करेगा। और यह प्रश्न स्वयं प्रतिनिधियों / घटनाओं के विषय को उठाता है।
मार्क Gravell

यह कोड कहीं एक अनाम विधि उत्पन्न करता है, जो वास्तव में किसी भी अपवाद के ढेर का पता लगाता है। क्या अनाम तरीकों को नाम देना संभव है? ;)
sisve

2015 / C # 6.0 के अनुसार गलत ...? क्या वह एकांत है। ReferenceTHatMayBeNull? .CallMethod () शून्य होने पर विधि को कॉल नहीं करेगा।
टॉमटॉम

1
@mercu यह होना चाहिए ?.- VB14 में और इसके बाद के संस्करण
मार्क Gravell

27

क्या आप के लिए देख रहे हैं नल सशर्त (नहीं "coalescing") ऑपरेटर है ?.:। यह C # 6 के रूप में उपलब्ध है।

आपका उदाहरण होगा obj?.SomeMethod();। अगर obj अशक्त है, तो कुछ भी नहीं होता है। जब विधि में तर्क होते हैं, जैसे obj?.SomeMethod(new Foo(), GetBar());कि यदि objअशक्त है तो तर्कों का मूल्यांकन नहीं किया जाता है, जो कि अगर तर्क का मूल्यांकन करता है तो दुष्प्रभाव होंगे।

और चैनिंग संभव है: myObject?.Items?[0]?.DoSomething()


1
यह शानदार है। यह ध्यान देने योग्य है कि यह C # 6 फीचर है ... (जो आपके VS2015 स्टेटमेंट से निहित होगा, लेकिन फिर भी ध्यान देने योग्य है)। :)
काइल गूडे

10

एक त्वरित विस्तार विधि:

    public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
        if(obj != null) {
            action(obj);
        } else if ( actionIfNull != null ) {
            actionIfNull();
        }
    }

उदाहरण:

  string str = null;
  str.IfNotNull(s => Console.Write(s.Length));
  str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));

या वैकल्पिक रूप से:

    public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
        return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
    }

उदाहरण:

    string str = null;
    Console.Write(str.IfNotNull(s => s.Length.ToString());
    Console.Write(str.IfNotNull(s => s.Length.ToString(), () =>  "null"));

मेरे पास इसे विस्तार के तरीकों के साथ करने का प्रयास था और मैं बहुत ही समान कोड के साथ समाप्त हुआ। हालाँकि, इस कार्यान्वयन के साथ समस्या यह है कि यह उन भावों के साथ काम नहीं करेगा जो एक मूल्य प्रकार लौटाते हैं। तो उसके लिए दूसरा तरीका होना चाहिए था।
orad

4

घटनाओं को एक खाली डिफ़ॉल्ट प्रतिनिधि के साथ आरंभ किया जा सकता है जिसे कभी हटाया नहीं जाता है:

public event EventHandler MyEvent = delegate { };

कोई शून्य-जाँच आवश्यक नहीं।

[ अपडेट , यह बताने के लिए बेवन का धन्यवाद]

हालांकि संभावित प्रदर्शन प्रभाव से अवगत रहें। एक त्वरित माइक्रो बेंचमार्क जो मैंने किया था कि "डिफ़ॉल्ट प्रतिनिधि" पैटर्न का उपयोग करते समय बिना किसी सब्सक्राइबर के किसी ईवेंट को हैंडल करना 2-3 गुना धीमा है। (मेरे दोहरे कोर 2.5GHz लैपटॉप पर जिसका अर्थ है 279ms: 50 मिलियन नॉट-सब्सक्राइब इवेंट्स बढ़ाने के लिए 785ms।)। अनुप्रयोग हॉट स्पॉट के लिए, यह विचार करने के लिए एक मुद्दा हो सकता है।


1
तो, आप एक खाली प्रतिनिधि को आमंत्रित करके एक अशक्त-जांच से बचते हैं ... कुछ कीस्ट्रोक्स को बचाने के लिए स्मृति और समय दोनों का औसत दर्जे का बलिदान? YMMV, लेकिन मेरे लिए, एक खराब व्यापार बंद।
बेवन

मैंने ऐसे बेंचमार्क भी देखे हैं जो दिखाते हैं कि किसी एक के साथ एक सब्सक्राइब करने वाले कई प्रतिनिधियों के साथ किसी इवेंट को लागू करने के लिए यह काफी महंगा है।
ग्रेग डी


2

इयान ग्रिफिथ्स का यह लेख समस्या के दो अलग-अलग समाधान देता है जो वह निष्कर्ष निकालता है कि आप साफ-सुथरी चालें हैं जिनका आपको उपयोग नहीं करना चाहिए।


3
और उस लेख के लेखक के रूप में बोलते हुए, मैं यह जोड़ना चाहूंगा कि आप निश्चित रूप से अब इसका उपयोग नहीं करना चाहिए कि C # 6 इसे शून्य सशर्त संचालकों (? और? []] के साथ बॉक्स से बाहर निकालता है।
इयान ग्रिफिथ्स

2

सुझाव की तरह विलोपन विधि का पालन करना वास्तव में दौड़ की स्थिति के साथ मुद्दों को हल नहीं करता है, बल्कि उन्हें छिपाता है।

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}

जैसा कि कहा गया है कि यह कोड अस्थायी चर के साथ समाधान के लिए सुरुचिपूर्ण समकक्ष है, लेकिन ...

दोनों के साथ समस्या है यह है यह संभव है कि घटना के सबसेंटर को AFTER कहा जा सकता है इसे घटना से हटा दिया गया है । यह संभव है क्योंकि डेलिगेशन टेंपरेचर चर (या ऊपर विधि में पैरामीटर के रूप में पारित) में कॉपी किए जाने के बाद सदस्यता समाप्त हो सकता है, लेकिन प्रतिनिधि के आने से पहले।

सामान्य तौर पर ग्राहक कोड का व्यवहार इस तरह के मामले में अप्रत्याशित है: घटक राज्य पहले से ही घटना सूचना को संभालने की अनुमति नहीं दे सकता है। क्लाइंट कोड को इसे संभालने के तरीके में लिखना संभव है, लेकिन यह क्लाइंट को अनावश्यक रूप से ज़िम्मेदारी देगा।

थ्रेड सेफ़िटी सुनिश्चित करने का एकमात्र ज्ञात तरीका ईवेंट के प्रेषक के लिए लॉक स्टेटमेंट का उपयोग करना है। यह सुनिश्चित करता है कि सभी सदस्यताएँ \ सदस्यता रद्द \ "मंगलाचरण क्रमबद्ध हैं।

अधिक सटीक लॉक करने के लिए एक ही सिंक ऑब्जेक्ट में add \ remove इवेंट एक्सेसर विधियों का उपयोग किया जाना चाहिए जो डिफ़ॉल्ट रूप से 'यह' है।


यह दौड़ की स्थिति नहीं है जिसे संदर्भित किया जाता है। दौड़ की स्थिति है अगर (MyEvent! = Null) // MyEvent अब शून्य नहीं है MyEvent.Invoke (); // MyEvent अब अशक्त है, बुरी चीजें होती हैं इसलिए इस दौड़ की स्थिति के साथ, मैं एक एसिंक्स इवेंट हैंडलर नहीं लिख सकता हूं जो काम करने की गारंटी है। हालाँकि, आपकी 'रेस कंडीशन' के साथ, मैं एक एसिंक्स ईवेंट हैंडलर लिख सकता हूं जो काम करने की गारंटी है। बेशक सब्सक्राइबर और अनसब्सक्राइबर को कॉल को सिंक्रोनस करने की आवश्यकता है, या लॉक कोड की आवश्यकता है।
ज्योंगू

वास्तव में। इस समस्या का मेरा विश्लेषण यहाँ पोस्ट किया गया है: blogs.msdn.com/ericlippert/archive/2009/04/29/…
एरिक


1

शायद बेहतर नहीं है, लेकिन मेरी राय में अधिक पठनीय एक विस्तार विधि बनाना है

public static bool IsNull(this object obj) {
 return obj == null;
}

1
क्या return obj == nullमतलब है यह क्या लौटेगा
हम्माद खान

1
इसका मतलब है कि अगर objहै nullविधि वापस आ जाएगी true, मुझे लगता है।
जोएल

1
यह कैसे भी सवाल का जवाब देता है?
कुगेल

देर से जवाब, लेकिन क्या आपको यकीन है कि यह भी काम करेगा? क्या आप किसी ऑब्जेक्ट पर एक्सटेंशन विधि कह सकते हैं null? मुझे पूरा यकीन है कि यह प्रकार के अपने तरीकों से काम नहीं करता है, इसलिए मुझे संदेह है कि यह विस्तार विधियों के साथ भी काम करेगा। मुझे विश्वास है कि अगर कोई वस्तु है तो जांचने का सबसे अच्छा तरीका nullहै obj is null। दुर्भाग्य से, यह जांचने के लिए कि क्या किसी वस्तु कोnull कोष्ठक में लपेटने की आवश्यकता नहीं है, जो दुर्भाग्यपूर्ण है।
नैटिक्स

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.