C # में नल पैरामीटर जाँच


85

C # में, हर फ़ंक्शन में पैरामीटर null चेक जोड़ने के लिए कोई बेहतर कारण (एक बेहतर त्रुटि संदेश के अलावा) हैं जहाँ null एक मान्य मान नहीं है? जाहिर है, कोड का उपयोग करता है कि वैसे भी एक अपवाद फेंक देंगे। और इस तरह के चेक कोड को धीमा और बनाए रखने के लिए कठिन बनाते हैं।

void f(SomeType s)
{
  if (s == null)
  {
    throw new ArgumentNullException("s cannot be null.");
  }

  // Use s
}

12
मुझे गंभीरता से संदेह है कि एक साधारण नल चेक आपके कोड को एक महत्वपूर्ण (या यहां तक ​​कि औसत दर्जे की) राशि से धीमा कर देगा।
Heinzi

खैर, यह निर्भर करता है कि "यूज़ एस" क्या करता है। यदि यह सब "रिटर्न एस.सोमफिल्ड" है, तो अतिरिक्त जांच संभवतः परिमाण के एक क्रम से विधि को धीमा कर देगी।
कलस

10
@कालस: शायद? शायद यह मेरी किताब में कटौती नहीं करता है। आपका परीक्षण डेटा कहां है? और यह कैसे संभव है कि इस तरह का बदलाव पहली बार में आपके आवेदन की अड़चन होगी?
जॉन स्कीट

@Jon: तुम सही हो मैं यहाँ मुश्किल डेटा नहीं है। यदि आप विंडोज फोन या एक्सबॉक्स के लिए विकसित होते हैं जहां जेआईटी इनलाइनिंग इस "यदि" से प्रभावित होगी, और जहां ब्रांच करना बहुत महंगा है, तो आप काफी पागल हो सकते हैं।
कालस

3
@kaalus: तो अपनी कोड शैली को उन विशिष्ट मामलों में समायोजित करें जो यह साबित करने के बाद कि यह एक महत्वपूर्ण अंतर बनाता है या डीबग का उपयोग करता है। (फिर से, केवल उन स्थितियों में, एरिक का जवाब देखें) ताकि चेक केवल कुछ बिल्ड में हो।
जॉन स्कीट

जवाबों:


163

हां, इसके अच्छे कारण हैं:

  • यह वास्तव में क्या अशक्त है, जो एक से स्पष्ट नहीं हो सकता है की पहचान करता है NullReferenceException
  • यह अमान्य इनपुट पर कोड को विफल कर देता है, भले ही कुछ अन्य स्थिति का मतलब है कि मूल्य को कम नहीं किया गया है
  • यह इस अपवाद को उत्पन्न करता है इससे पहले कि आप किसी अन्य दुष्परिणाम हो सकते हैं, जो आपके पहले प्रभाव से पहले हो सकता है
  • इसका मतलब है कि आप आश्वस्त हो सकते हैं कि यदि आप पैरामीटर को किसी और चीज में पास करते हैं, तो आप उनके अनुबंध का उल्लंघन नहीं कर रहे हैं
  • यह आपकी विधि की आवश्यकताओं को दस्तावेज करता है ( कोड अनुबंध का उपयोग करना , पाठ्यक्रम के लिए और भी बेहतर है)

अब अपनी आपत्तियों के लिए:

  • यह धीमा है : क्या आपने पाया है कि यह वास्तव में आपके कोड में अड़चन है, या आप अनुमान लगा रहे हैं? तुच्छता चेकों बहुत जल्दी कर रहे हैं, और अधिकांश मामलों में वे कर रहे हैं नहीं टोंटी होने जा रहा
  • यह कोड को बनाए रखने के लिए कठिन बनाता है : मुझे लगता है कि इसके विपरीत। मुझे लगता है कि यह कोड का उपयोग करना आसान है, जहां यह स्पष्ट है कि कोई पैरामीटर अशक्त हो सकता है या नहीं, और जहां आप आश्वस्त हैं कि यह स्थिति लागू है।

और आपके दावे के लिए:

जाहिर है, कोड का उपयोग करता है कि वैसे भी एक अपवाद फेंक देंगे।

वास्तव में? विचार करें:

void f(SomeType s)
{
  // Use s
  Console.WriteLine("I've got a message of {0}", s);
}

यह उपयोग करता है s, लेकिन यह एक अपवाद नहीं है। यदि यह sअशक्त होने के लिए अमान्य है , और यह इंगित करता है कि कुछ गलत है, तो एक अपवाद यहां सबसे उपयुक्त व्यवहार है।

अब जहाँ आप उन तर्क सत्यापन जाँचों को रखते हैं वह एक अलग मामला है। आप अपने स्वयं के वर्ग के भीतर सभी कोड पर भरोसा करने का निर्णय ले सकते हैं, इसलिए निजी तरीकों पर परेशान न हों। आप अपनी बाकी विधानसभा पर भरोसा करने का फैसला कर सकते हैं, इसलिए आंतरिक तरीकों पर ध्यान न दें। आपको सार्वजनिक विधियों के तर्कों को लगभग निश्चित रूप से मान्य करना चाहिए।

एक साइड नोट: सिंगल-पैरामीटर कंस्ट्रक्टर ओवरलोड का ArgumentNullExceptionसिर्फ पैरामीटर नाम होना चाहिए, इसलिए आपका टेस्ट होना चाहिए:

if (s == null)
{
  throw new ArgumentNullException("s");
}

वैकल्पिक रूप से आप एक विस्तार विधि बना सकते हैं, जिससे कुछ हद तक तनाव हो सकता है:

s.ThrowIfNull("s");

मेरे (जेनेरिक) एक्सटेंशन विधि के संस्करण में, मैं इसे मूल मूल्य वापस कर देता हूं यदि यह गैर अशक्त है, तो आप चीजों को लिखने की अनुमति देते हैं:

this.name = name.ThrowIfNull("name");

आपके पास एक अधिभार हो सकता है जो पैरामीटर नाम नहीं लेता है, यदि आप उस बारे में बहुत परेशान नहीं हैं।


8
एक्सटेंशन विधि के लिए +1। मैं सटीक एक ही बात किया है जैसे अन्य सत्यापन सहित, ThrowIfEmptyपरICollection
Davy8

5
क्यों मुझे लगता है कि इसे बनाए रखना कठिन है: हर मैनुअल तंत्र के साथ जिसे हर बार प्रोग्रामर के हस्तक्षेप की आवश्यकता होती है, यह गलतियों और चूक का संकेत है और कोड को काटता है। परतदार सुरक्षा किसी भी सुरक्षा से बदतर है।
कालस

8
@kaalus: क्या आप परीक्षण के लिए समान रवैया लागू करते हैं? "मेरे परीक्षण सभी संभव कीड़े नहीं पकड़ेंगे, इसलिए मैं कोई भी लिखूंगा नहीं"? सुरक्षा तंत्र जब ऐसा काम करते हैं, वे यह आसान समस्या को खोजने के लिए बनाने के लिए और कहीं और प्रभाव को कम करने (पहले समस्या को पकड़ने से) करेंगे। यदि ऐसा 10 में से 9 बार होता है, तो यह 10 में से 0 बार होने से भी बेहतर है ...
जॉन स्कीट

2
@DoctorOreo: मैं उपयोग नहीं करता Debug.Assert। यह विकास में त्रुटियों को पकड़ने के लिए और भी अधिक महत्वपूर्ण है (इससे पहले कि वे वास्तविक डेटा को खराब कर दें) विकास की तुलना में।
जॉन स्कीट

2
अब C # 6.0 के साथ हम उपयोग कर सकते हैं throw new ArgumentNullException(nameof(s))
hrzafer

52

मैं जॉन से सहमत हूं, लेकिन मैं इसमें एक बात जोड़ूंगा।

स्पष्ट अशक्त जाँच जोड़ने के बारे में मेरा दृष्टिकोण इन परिसरों पर आधारित है:

  • किसी प्रोग्राम में प्रत्येक स्टेटमेंट का अभ्यास करने के लिए आपकी यूनिट परीक्षणों का एक तरीका होना चाहिए।
  • throwबयान कर रहे हैं बयान
  • एक का परिणाम ifएक बयान है
  • इसलिए, व्यायाम करने के लिए एक तरह से किया जाना चाहिए throwमेंif (x == null) throw whatever;

यदि उस कथन को निष्पादित करने के लिए कोई संभव तरीका नहीं है, तो इसका परीक्षण नहीं किया जा सकता है और इसे प्रतिस्थापित किया जाना चाहिए Debug.Assert(x != null);

यदि उस कथन को निष्पादित करने के लिए एक संभावित तरीका है, तो बयान लिखें, और फिर एक इकाई परीक्षण लिखें जो इसे अभ्यास करता है।

यह विशेष रूप से महत्वपूर्ण है कि सार्वजनिक प्रकार के सार्वजनिक तरीके इस तरह से अपने तर्कों की जांच करते हैं; आपको कोई अंदाजा नहीं है कि आपके उपयोगकर्ता क्या पागल करने वाले हैं। उन्हें दे दो "अरे तुम हड्डी हो, तुम गलत कर रहे हो!" जितनी जल्दी हो सके अपवाद।

निजी प्रकार के निजी तरीके, इसके विपरीत, उस स्थिति में होने की बहुत अधिक संभावना है जहां आप तर्कों को नियंत्रित करते हैं और इसकी मजबूत गारंटी हो सकती है कि तर्क कभी भी अशक्त नहीं है; दस्तावेज़ के लिए एक जोर का उपयोग करें कि अपरिवर्तनीय।


22

मैं अब एक साल से इसका उपयोग कर रहा हूं:

_ = s ?? throw new ArgumentNullException(nameof(s));

यह एक oneliner है, और त्याग ( _) का मतलब है कि कोई अनावश्यक आवंटन नहीं है।


8

एक स्पष्ट बिना ifजांच, यह पता लगाने के लिए बहुत मुश्किल हो सकता क्या था nullअगर आप कोड स्वामी नहीं हैं।

यदि आपको NullReferenceExceptionस्रोत कोड के बिना किसी लाइब्रेरी के अंदर गहरे से मिलता है , तो आपको यह पता लगाने में बहुत परेशानी होगी कि आपने क्या गलत किया।

ये ifचेक आपके कोड को बिल्कुल धीमा नहीं करेंगे।


ध्यान दें कि ArgumentNullExceptionकंस्ट्रक्टर का पैरामीटर एक पैरामीटर नाम है, संदेश नहीं।
आपका कोड होना चाहिए

if (s == null) throw new ArgumentNullException("s");

मैंने इसे आसान बनाने के लिए एक कोड स्निपेट लिखा:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Check for null arguments</Title>
            <Shortcut>tna</Shortcut>
            <Description>Code snippet for throw new ArgumentNullException</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Parameter</ID>
                    <ToolTip>Paremeter to check for null</ToolTip>
                    <Default>value</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$");
        $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

5

यदि आप एक पैरामीटर के रूप में किसी भी अशक्त वस्तुओं को प्राप्त नहीं करते हैं, तो आपको यह सुनिश्चित करने के लिए कोड अनुबंध पर एक नज़र डालने की आवश्यकता हो सकती है।


2

मुख्य लाभ यह है कि आप शुरू से ही अपने तरीके की आवश्यकताओं के साथ स्पष्ट हैं। इससे कोड पर काम करने वाले अन्य डेवलपर्स को यह स्पष्ट हो जाता है कि कॉल करने वाले के लिए यह सही मायने में एक त्रुटि है कि वह आपके तरीके को एक शून्य मान भेज सकता है।

चेक किसी अन्य कोड को निष्पादित करने से पहले विधि के निष्पादन को भी रोक देगा। इसका मतलब है कि आपको उस विधि से किए गए संशोधनों के बारे में चिंता करने की ज़रूरत नहीं है जो अधूरी रह गई हैं।


2

यह कुछ डीबगिंग को बचाता है, जब आप उस अपवाद को मारते हैं।

ArgumentNullException में स्पष्ट रूप से कहा गया है कि यह "s" था जो अशक्त था।

यदि आपके पास वह चेक नहीं है और कोड को अस्पष्ट होने दें, तो आपको उस विधि में कुछ अज्ञात रेखा से NullReferenceException मिलती है। एक रिलीज बिल्ड में आपको लाइन नंबर नहीं मिलते हैं!


0

मूल कोड:

void f(SomeType s)
{
  if (s == null)
  {
    throw new ArgumentNullException("s cannot be null.");
  }

  // Use s
}

इसे फिर से लिखें:

void f(SomeType s)
{
  if (s == null) throw new ArgumentNullException(nameof(s));
}

[संपादित करें] उपयोग करने के लिए फिर से लिखने का nameofकारण यह है कि यह आसान रीफैक्टरिंग के लिए अनुमति देता है। यदि आपके चर sका नाम कभी बदलता है तो डिबगिंग संदेशों को भी अपडेट किया जाएगा, जबकि यदि आप केवल चर के नाम को हार्डकोड करते हैं तो यह अंततः पुराना हो जाएगा जब अपडेट समय के साथ किए जाते हैं। यह उद्योग में इस्तेमाल होने वाला एक अच्छा अभ्यास है।


यदि आप एक बदलाव का सुझाव दे रहे हैं, तो क्यों बताएं। यह भी सुनिश्चित करें कि आप उस प्रश्न का उत्तर दे रहे हैं जो पूछा जा रहा है।
कोडकास्टर

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