क्या अशक्त या खाली संग्रह को वापस करना बेहतर है?


420

यह एक सामान्य प्रश्न की तरह है (लेकिन मैं C # का उपयोग कर रहा हूं), सबसे अच्छा तरीका क्या है (सबसे अच्छा अभ्यास), क्या आप एक विधि के लिए अशक्त या खाली संग्रह लौटाते हैं जिसमें एक वापसी प्रकार के रूप में एक संग्रह है?


5
उम, काफी CPerkins नहीं। rads.stackoverflow.com/amzn/click/0321545613

3
@ सहकर्मी - हाँ, वहाँ है। यह Microsoft के अपने .NET फ्रेमवर्क डिज़ाइन दिशानिर्देशों में स्पष्ट रूप से कहा गया है। विवरण के लिए रिचर्डोड का जवाब देखें।
ग्रेग बिच

51
केवल अगर अर्थ "मैं परिणामों की गणना नहीं कर सकता" तो क्या आपको अशक्त होना चाहिए। नल को कभी भी "खाली", केवल "लापता" या "अज्ञात" का शब्दार्थ नहीं होना चाहिए। इस विषय पर मेरे लेख में अधिक जानकारी: blogs.msdn.com/ericlippert/archive/2009/05/14/…
एरिक

3
Bozho: "कोई भी" भाषाओं का एक बहुत कुछ है। आम लिस्प के बारे में क्या है, जहां खाली सूची शून्य मान के बराबर है? :-)
केन

9
दरअसल, "डुप्लिकेट" उन तरीकों के बारे में है जो एक वस्तु लौटाते हैं, संग्रह नहीं। यह अलग जवाब के साथ एक अलग परिदृश्य है।
गांगेय

जवाबों:


499

खाली संग्रह। हमेशा।

यह बेकार है:

if(myInstance.CollectionProperty != null)
{
  foreach(var item in myInstance.CollectionProperty)
    /* arrgh */
}

nullकिसी संग्रह या संग्रहणीय को वापस करते समय वापस लौटने के लिए यह सबसे अच्छा अभ्यास माना जाता है । हमेशा एक खाली गणना / संग्रह लौटाते हैं। यह पूर्वोक्त बकवास को रोकता है, और आपकी कार को आपकी कक्षाओं के सहकर्मियों और उपयोगकर्ताओं द्वारा प्राप्त होने से रोकता है।

गुणों के बारे में बात करते समय, हमेशा अपनी संपत्ति को एक बार सेट करें और इसे भूल जाएं

public List<Foo> Foos {public get; private set;}

public Bar() { Foos = new List<Foo>(); }

.NET 4.6.1 में, आप इसे बहुत कम कर सकते हैं:

public List<Foo> Foos { get; } = new List<Foo>();

जब एन्यूमरैबल्स लौटाने वाले तरीकों के बारे में बात कर रहे हैं, तो आप आसानी से एक खाली एनीमेरेबल को वापस कर सकते हैं null...

public IEnumerable<Foo> GetMyFoos()
{
  return InnerGetFoos() ?? Enumerable.Empty<Foo>();
}

उपयोग करने Enumerable.Empty<T>()को लौटने की तुलना में अधिक कुशल के रूप में देखा जा सकता है, उदाहरण के लिए, एक नया खाली संग्रह या सरणी।


24
मैं विल से सहमत हूं, लेकिन मुझे लगता है कि "हमेशा" थोड़ा अधिक है। जबकि एक खाली संग्रह का अर्थ "0 आइटम" हो सकता है, वापस लौटने का अर्थ हो सकता है "सभी पर कोई संग्रह नहीं" - उदा। यदि आप HTML को पार्स कर रहे हैं, तो id = "foo" के साथ <ul>, <ul id = "foo"> </ ul> की तलाश कर खाली संग्रह लौटा सकते हैं; अगर आईडी के साथ कोई <ul> नहीं है = "foo" एक अशक्त वापसी बेहतर होगी (जब तक कि आप इस मामले को अपवाद के साथ नहीं संभालना चाहते हैं)
Patonza

30
यह हमेशा एक सवाल नहीं है कि "आप आसानी से एक खाली सरणी वापस कर सकते हैं", लेकिन वर्तमान संदर्भ में कोई खाली सरणी भ्रामक हो सकती है या नहीं। खाली सरणी वास्तव में कुछ का मतलब है, जैसा कि अशक्त है। यह कहने के लिए कि आपको हमेशा शून्य के बजाय एक खाली सरणी वापस करनी चाहिए, लगभग यह कहकर गुमराह किया जाता है कि आपको बूलियन पद्धति हमेशा सही होनी चाहिए। दोनों संभावित मूल्य एक अर्थ व्यक्त करते हैं।
डेविड हेडलंड

31
आपको वास्तव में नई Foo [0] के बजाय System.Linq.Enumerable.Empty <Foo> () लौटना पसंद करना चाहिए। यह अधिक स्पष्ट है और आपको एक मेमोरी आवंटन (कम से कम, मेरे स्थापित .NET कार्यान्वयन में) बचाता है।
ट्रिलियन २

4
@Ill: ओपी: "क्या आप एक विधि के लिए रिक्त या खाली संग्रह लौटाते हैं, जिसमें रिटर्न प्रकार के रूप में एक संग्रह है"। मुझे लगता है कि इस मामले में यह बहुत मायने रखता है IEnumerableया ICollectionनहीं। वैसे भी, यदि आप कुछ प्रकार का चयन करते हैं तो ICollectionवे भी लौट जाते हैं null... मैं चाहूंगा कि वे एक खाली संग्रह लौटाएं, लेकिन मैं उन्हें लौटाने में भाग गया null, इसलिए मैंने सोचा कि मैं यहां इसका उल्लेख करूंगा। मैं कहूंगा कि असंख्य संग्रह का डिफ़ॉल्ट खाली नहीं है। मुझे नहीं पता था कि यह इतना संवेदनशील विषय था।
मैथिज्स वेसल्स


154

से फ्रेमवर्क डिजाइन दिशानिर्देश 2 संस्करण (पृष्ठ 256।):

संग्रह गुणों से या संग्रह रिटर्न करने के तरीकों से शून्य मान वापस न करें। इसके बजाय एक खाली संग्रह या एक खाली सरणी लौटें।

यहाँ एक और दिलचस्प लेख है, अशक्त न लौटने के लाभों पर (मैं ब्रैड अब्राम के ब्लॉग पर कुछ खोजने की कोशिश कर रहा था, और वह लेख से जुड़ा हुआ है)।

संपादित करें- जैसा कि एरिक लिपर्ट ने अब मूल प्रश्न पर टिप्पणी की है, मैं उनके उत्कृष्ट लेख से लिंक करना चाहूंगा ।


33
+1। जब तक आपके पास बहुत अच्छा कारण नहीं है, तब तक फ्रेमवर्क डिज़ाइन दिशानिर्देशों का पालन करना हमेशा सबसे अच्छा अभ्यास होता है।

@Will, बिल्कुल। यही मेरा अनुसरण है। मैंने कभी भी अन्यथा करने की कोई आवश्यकता नहीं पाई है
रिचर्ड डिक ने

हाँ, यह वही है जो आप का पालन करें। लेकिन ऐसे मामलों में, जिन एपीआई पर आप भरोसा करते हैं, वे उसका पालन नहीं करते हैं, आप 'फंसे हुए हैं';)
बूझो

@ बूझो- यीप। आपका जवाब उन फ्रिंज मामलों के कुछ अच्छे उत्तर प्रदान करता है।
रिचर्ड डेस

90

आपके अनुबंध और आपके ठोस मामले पर निर्भर करता है । आम तौर पर खाली संग्रह वापस करना सबसे अच्छा है , लेकिन कभी-कभी ( शायद ही कभी ):

  • null कुछ अधिक विशिष्ट मतलब हो सकता है;
  • आपका API (अनुबंध) आपको वापस जाने के लिए मजबूर कर सकता है null

कुछ ठोस उदाहरण:

  • एक यूआई घटक (आपके नियंत्रण से बाहर एक पुस्तकालय से), एक खाली संग्रह प्रदान कर सकता है यदि कोई खाली संग्रह पारित किया जाता है, या कोई तालिका खाली नहीं है, अगर शून्य पास किया गया है।
  • एक ऑब्जेक्ट-टू-एक्सएमएल (JSON / जो भी हो) में, जहां nullइसका अर्थ होगा कि तत्व गायब है, जबकि एक खाली संग्रह एक निरर्थक (और संभवतः गलत) को प्रस्तुत करेगा।<collection />
  • आप एक एपीआई का उपयोग कर रहे हैं या उसे लागू कर रहे हैं जो स्पष्ट रूप से बताता है कि नल को वापस / पास होना चाहिए

4
@ Bozho- यहाँ कुछ दिलचस्प उदाहरण।
रिचर्ड डेस

1
मुझे आपकी बात सही लगी, मुझे नहीं लगता कि आपको कोड को अन्य दोष के आसपास बनाना चाहिए और परिभाषा में 'n' को c # में कभी कुछ विशिष्ट करने का मतलब नहीं है। मान नल को "कोई जानकारी नहीं" के रूप में परिभाषित किया गया है और इसलिए यह कहना है कि यह विशिष्ट जानकारी का वहन करता है एक ऑक्सीमोरोन है। यही कारण है कि .NET दिशानिर्देश कहते हैं कि यदि सेट वास्तव में खाली है तो आपको एक खाली सेट वापस करना चाहिए। लौटने वाला अशक्त कह रहा है: "मुझे नहीं पता कि अपेक्षित सेट कहाँ चला गया"
रुना एफएस

13
नहीं, इसका अर्थ है "सेट का कोई तत्व नहीं है" के बजाय "कोई सेट नहीं है"
Bozho

मुझे विश्वास है कि, तब मुझे बहुत सारे टीएसक्यूएल लिखना पड़ा और सीखा कि हमेशा ऐसा नहीं होता, हेह।

1
ऑब्जेक्ट-टू-एक्सएमएल वाला हिस्सा स्पॉट पर है
मिहाई कारकोस्टिया

36

एक अन्य बिंदु है जिसका अभी तक उल्लेख नहीं किया गया है। निम्नलिखित कोड पर विचार करें:

    public static IEnumerable<string> GetFavoriteEmoSongs()
    {
        yield break;
    }

इस पद्धति को कॉल करने पर C # भाषा एक खाली एन्यूमरेटर लौटाएगी। इसलिए, भाषा के डिजाइन के अनुरूप (और, इस प्रकार, प्रोग्रामर अपेक्षाएं) एक खाली संग्रह लौटाया जाना चाहिए।


1
मैं तकनीकी रूप से यह नहीं सोचता कि यह एक खाली संग्रह है।
फ्राईग्यु

3
@FryGuy - नहीं, यह नहीं है। यह एक Enumerable ऑब्जेक्ट देता है, जिसका GetEnumerator () विधि एक Enumerator देता है जो (एक संग्रह के साथ एनालेगी द्वारा) खाली होता है। अर्थात्, एन्यूमरेटर के मूवनेक्स्ट () विधि हमेशा झूठी होती है। उदाहरण विधि को कॉल करने से अशक्त नहीं लौटता है, और न ही वह एन्यूमरेबल ऑब्जेक्ट लौटाता है जिसका गेटइंटरमीटर () विधि रिटर्न को सुस्त बनाती है।
जेफरी एल व्हाइटलेज

31

खाली अधिक उपभोक्ता के अनुकूल है।

खाली गणना करने की एक स्पष्ट विधि है:

Enumerable.Empty<Element>()

2
साफ सुथरी चाल के लिए धन्यवाद। मैं एक बेवकूफ की तरह एक खाली सूची <T> () को तुरंत हटा रहा था लेकिन यह बहुत साफ दिखता है और शायद थोड़ा अधिक कुशल भी है।
जैकब्स सॉल्यूशंस

18

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

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

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


सॉन्डर्स को उत्तर दें:

हां, मैं मान रहा हूं कि "व्यक्ति ने सवाल का जवाब नहीं दिया" और "उत्तर शून्य था।" वह मेरे उत्तर के अंतिम पैराग्राफ का बिंदु था। कई कार्यक्रम रिक्त या शून्य से "पता नहीं" भेद करने में असमर्थ हैं, जो मुझे एक संभावित गंभीर दोष लगता है। उदाहरण के लिए, मैं एक साल पहले या एक घर के लिए खरीदारी कर रहा था। मैं एक रियल एस्टेट वेब साइट पर गया और वहाँ $ 0 के पूछ मूल्य के साथ सूचीबद्ध कई घर थे। मेरे लिए बहुत अच्छा लग रहा है: वे इन घरों को मुफ्त में दे रहे हैं! लेकिन मुझे यकीन है कि दुखद वास्तविकता यह थी कि वे अभी कीमत में नहीं आए थे। उस स्थिति में, आप कह सकते हैं, "ठीक है, OBVIOUSLY शून्य का मतलब है कि उन्होंने कीमत दर्ज नहीं की है - किसी को भी मुफ्त में घर नहीं देना है।" लेकिन साइट ने विभिन्न शहरों में घरों की औसत पूछ और बिक्री को भी सूचीबद्ध किया। मैं मदद नहीं कर सकता, लेकिन आश्चर्य है कि अगर औसत में शून्य शामिल नहीं है, तो इस तरह से कुछ स्थानों के लिए गलत रूप से कम औसत देता है। यानी $ 100,000 का औसत क्या है; $ 120,000; और "पता नहीं"? तकनीकी रूप से इसका उत्तर "पता नहीं" है। हम जो वास्तव में देखना चाहते हैं वह $ 110,000 है। लेकिन जो हमें मिलेगा वह $ 73,333 है, जो पूरी तरह से गलत होगा। इसके अलावा, क्या होगा अगर हमें ऐसी साइट पर समस्या है जहां उपयोगकर्ता ऑन-लाइन ऑर्डर कर सकते हैं? (अचल संपत्ति के लिए पूरी तरह से, लेकिन मुझे यकीन है कि आपने इसे कई अन्य उत्पादों के लिए देखा है।) क्या हम वास्तव में "मुक्त" के रूप में व्याख्या किए जाने के लिए "मूल्य अभी तक निर्दिष्ट नहीं" चाहते हैं? इस प्रकार कुछ स्थानों के लिए गलत तरीके से कम औसत देना। यानी $ 100,000 का औसत क्या है; $ 120,000; और "पता नहीं"? तकनीकी रूप से इसका उत्तर "पता नहीं" है। हम जो वास्तव में देखना चाहते हैं वह $ 110,000 है। लेकिन जो हमें मिलेगा वह $ 73,333 है, जो पूरी तरह से गलत होगा। इसके अलावा, क्या होगा अगर हमें ऐसी साइट पर समस्या है जहां उपयोगकर्ता ऑन-लाइन ऑर्डर कर सकते हैं? (अचल संपत्ति के लिए पूरी तरह से, लेकिन मुझे यकीन है कि आपने इसे कई अन्य उत्पादों के लिए देखा है।) क्या हम वास्तव में "मुक्त" के रूप में व्याख्या किए जाने के लिए "मूल्य अभी तक निर्दिष्ट नहीं" चाहते हैं? इस प्रकार कुछ स्थानों के लिए गलत तरीके से कम औसत देना। यानी $ 100,000 का औसत क्या है; $ 120,000; और "पता नहीं"? तकनीकी रूप से इसका उत्तर "पता नहीं" है। हम जो वास्तव में देखना चाहते हैं वह $ 110,000 है। लेकिन जो हमें मिलेगा वह $ 73,333 है, जो पूरी तरह से गलत होगा। इसके अलावा, क्या होगा अगर हमें ऐसी साइट पर समस्या है जहां उपयोगकर्ता ऑन-लाइन ऑर्डर कर सकते हैं? (अचल संपत्ति के लिए पूरी तरह से, लेकिन मुझे यकीन है कि आपने इसे कई अन्य उत्पादों के लिए देखा है।) क्या हम वास्तव में "मुक्त" के रूप में व्याख्या किए जाने के लिए "मूल्य अभी तक निर्दिष्ट नहीं" चाहते हैं? जो पूरी तरह से गलत होगा। इसके अलावा, क्या होगा अगर हमें ऐसी साइट पर समस्या है जहां उपयोगकर्ता ऑन-लाइन ऑर्डर कर सकते हैं? (अचल संपत्ति के लिए पूरी तरह से, लेकिन मुझे यकीन है कि आपने इसे कई अन्य उत्पादों के लिए देखा है।) क्या हम वास्तव में "मुक्त" के रूप में व्याख्या किए जाने के लिए "मूल्य अभी तक निर्दिष्ट नहीं" चाहते हैं? जो पूरी तरह से गलत होगा। इसके अलावा, क्या होगा अगर हमें ऐसी साइट पर समस्या है जहां उपयोगकर्ता ऑन-लाइन ऑर्डर कर सकते हैं? (अचल संपत्ति के लिए पूरी तरह से, लेकिन मुझे यकीन है कि आपने इसे कई अन्य उत्पादों के लिए देखा है।) क्या हम वास्तव में "मुक्त" के रूप में व्याख्या किए जाने के लिए "मूल्य अभी तक निर्दिष्ट नहीं" चाहते हैं?

आरई के दो अलग-अलग कार्य हैं, "क्या कोई है?" और "अगर ऐसा है, तो यह क्या है?" हाँ, आप निश्चित रूप से ऐसा कर सकते हैं, लेकिन आप क्यों करना चाहेंगे? अब कॉलिंग प्रोग्राम को एक के बजाय दो कॉल करने होंगे। यदि कोई प्रोग्रामर "कोई भी" कॉल करने में विफल रहता है तो क्या होगा? और सीधे "क्या है?" ? क्या कार्यक्रम एक गलत-अग्रणी शून्य लौटाएगा? एक अपवाद फेंको? अपरिभाषित मान लौटाएं? यह अधिक कोड, अधिक कार्य और अधिक संभावित त्रुटियां बनाता है।

एकमात्र लाभ जो मुझे दिखाई देता है वह यह है कि यह आपको एक मनमाना नियम का पालन करने में सक्षम बनाता है। क्या इस नियम का कोई फायदा है जो इसे पालन करने की परेशानी के लायक बनाता है? यदि नहीं, तो परेशान क्यों?


जेमाइक्सेस का जवाब दें:

विचार करें कि वास्तविक कोड कैसा दिखेगा। मुझे पता है कि प्रश्न C # है, लेकिन मुझे माफ करना अगर मैं जावा लिखता हूं। मेरा C # बहुत तेज नहीं है और सिद्धांत समान है।

एक अशक्त वापसी के साथ:

HospList list=patient.getHospitalizationList(patientId);
if (list==null)
{
   // ... handle missing list ...
}
else
{
  for (HospEntry entry : list)
   //  ... do whatever ...
}

एक अलग समारोह के साथ:

if (patient.hasHospitalizationList(patientId))
{
   // ... handle missing list ...
}
else
{
  HospList=patient.getHospitalizationList(patientId))
  for (HospEntry entry : list)
   // ... do whatever ...
}

यह वास्तव में अशक्त वापसी के साथ एक पंक्ति या दो कम कोड है, इसलिए यह कॉलर पर अधिक बोझ नहीं है, यह कम है।

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

ज़रूर, अगर कॉलर अशक्त के लिए जाँच नहीं करता है, तो प्रोग्राम एक अशक्त-सूचक अपवाद के साथ विफल हो सकता है। लेकिन अगर कोई अलग "फ़ंक्शन" मिला है और कॉलर उस फ़ंक्शन को कॉल नहीं करता है, लेकिन नेत्रहीन "सूची प्राप्त करें" फ़ंक्शन को कॉल करता है, तो क्या होता है? यदि यह एक अपवाद फेंकता है या अन्यथा विफल रहता है, ठीक है, यह बहुत अधिक है जैसा कि क्या होगा यदि यह अशक्त वापस आ गया और इसके लिए जांच नहीं की। यदि यह एक खाली सूची देता है, तो यह गलत है। आप "मेरे पास शून्य तत्वों के साथ एक सूची है" और "मेरे पास सूची नहीं है" के बीच अंतर करने में विफल रहे हैं। यह उस मूल्य के लिए शून्य वापस करने जैसा है जब उपयोगकर्ता ने कोई मूल्य दर्ज नहीं किया था: यह सिर्फ गलत है।

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

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


4
एक ही रिटर्न वैल्यू में "नो उत्तर" और "शून्य" प्रतिक्रियाओं को संयोजित करना क्यों आवश्यक है? इसके बजाय, विधि "पिछले पांच वर्षों में किसी भी पिछले अस्पताल में भर्ती" है, और एक अलग विधि है जो पूछती है, "क्या पिछले अस्पताल में भर्ती सूची कभी भरी हुई थी?"। यह मानते हैं कि पिछली अस्पताल में भर्ती नहीं की गई सूची और भरे हुए सूची के बीच अंतर है।
जॉन सॉन्डर्स

2
लेकिन अगर आप अशक्त लौटते हैं तो आप पहले से ही कॉलर पर एक अतिरिक्त बोझ डाल रहे हैं! कॉल करने वाले को अशक्त के लिए हर वापसी मूल्य की जांच करनी होती है - एक भयानक DRY उल्लंघन। आप इंगित करने के लिए चाहते हैं "फोन करने वाले सवाल का जवाब नहीं था" अलग है, यह है एक अतिरिक्त विधि कॉल तथ्य इंगित करने के लिए बनाने के लिए सरल। या तो वह, या एक अतिरिक्त संग्रह के साथ DeciltToAnswer की अतिरिक्त संपत्ति का उपयोग करें। अशक्त कभी नहीं लौटने का नियम बिल्कुल भी मनमाना नहीं है, यह कम से कम आश्चर्य का सिद्धांत है। साथ ही, आपके विधि के रिटर्न प्रकार का अर्थ यह होना चाहिए कि उसका नाम क्या कहता है। नल लगभग निश्चित रूप से नहीं करता है।
jammycakes

2
आप मान रहे हैं कि आपका getHospitalizationList केवल एक स्थान से और / या कहा जाता है कि इसके सभी कॉलर्स "कोई जवाब नहीं" और "शून्य" मामलों के बीच अंतर करना चाहते हैं। वहाँ होगा मामलों हो (लगभग निश्चित रूप से एक बहुमत) जहां अपने कॉलर उस भेद करने की जरूरत नहीं है, और इसलिए आप उन्हें स्थानों पर जहां यह आवश्यक नहीं होना चाहिए में अशक्त चेकों को जोड़ने के लिए मजबूर कर रहे हैं। यह आपके कोडबेस के लिए महत्वपूर्ण जोखिम जोड़ता है क्योंकि लोग बहुत आसानी से ऐसा करने के लिए भूल सकते हैं - और क्योंकि संग्रह के बजाय अशक्त होने के लिए बहुत कम वैध कारण हैं, यह बहुत अधिक संभावना होगी।
jammycakes

3
आरई नाम: कोई फ़ंक्शन नाम पूरी तरह से वर्णन नहीं कर सकता कि फ़ंक्शन तब तक क्या करता है जब तक कि यह फ़ंक्शन के रूप में लंबे समय तक न हो, और इस प्रकार बेतहाशा अव्यवहारिक। लेकिन किसी भी मामले में, यदि कोई जवाब नहीं दिए जाने पर फ़ंक्शन खाली सूची देता है, तो उसी तर्क से, इसे "getHospitalizationListOrEmptyListIfNoAnswer" नहीं कहा जाना चाहिए? लेकिन वास्तव में, क्या आप जोर देकर कहेंगे कि Java Reader.read फ़ंक्शन को readCharacterOrReturnMinusOneOnEndOfStream नाम दिया जाना चाहिए? वह ResultSet.getInt वास्तव में "getIntOrZeroIfValueWasNull" होना चाहिए? आदि
Jay

4
आरई हर कॉल को अलग करना चाहता है: ठीक है, हाँ, मैं यह मान रहा हूँ, या कम से कम एक फोन करने वाले के बारे में लेखक को सचेत निर्णय लेना चाहिए जिसका उसे कोई ध्यान नहीं है। यदि फ़ंक्शन "पता नहीं" के लिए एक खाली सूची देता है, और कॉल करने वाले नेत्रहीन इस "कोई नहीं" का इलाज करते हैं, तो यह गंभीर रूप से गलत परिणाम दे सकता है। कल्पना करें कि फ़ंक्शन "getAllergicReactionToMedicationList" था। एक प्रोग्राम जिसने नेत्रहीन रूप से इलाज किया "सूची में प्रवेश नहीं किया गया था" के रूप में "रोगी को कोई ज्ञात एलर्जी प्रतिक्रिया नहीं है" का शाब्दिक रूप से एक निधन को मार सकता है। यदि आपको कम नाटकीय परिणाम मिलते हैं, तो कई अन्य प्रणालियों में भी ऐसा ही होगा। ...
Jay

10

अगर एक खाली संग्रह अर्थपूर्ण रूप से समझ में आता है, तो यही है कि मैं वापसी करना पसंद करता हूं। GetMessagesInMyInbox()संचार के लिए एक खाली संग्रह लौटना "आपके इनबॉक्स में वास्तव में कोई संदेश नहीं है", जबकि लौटने से nullयह संप्रेषित करने के लिए उपयोगी हो सकता है कि अपर्याप्त डेटा यह कहने के लिए उपलब्ध है कि जिस सूची को लौटाया जाना चाहिए, वह देखने के लिए चाहिए।


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

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

6

वापसी करने वाला अधिक कुशल हो सकता है, क्योंकि कोई नई वस्तु नहीं बनाई गई है। हालाँकि, इसमें अक्सर nullचेक (या अपवाद हैंडलिंग) की भी आवश्यकता होती है ।

शब्दार्थ, nullऔर एक खाली सूची का मतलब एक ही बात नहीं है। अंतर सूक्ष्म हैं और एक उदाहरण विशिष्ट उदाहरणों में अन्य की तुलना में बेहतर हो सकता है।

अपनी पसंद के बावजूद, भ्रम से बचने के लिए इसे दस्तावेज़ करें।


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

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

सहमत, और ज्यादातर मामलों में यह अनुकूलन के लायक नहीं है। खाली सूची आधुनिक स्मृति प्रबंधन के साथ व्यावहारिक रूप से मुक्त है।
जेसन बेकर


4

हालात के उपर निर्भर। यदि यह एक विशेष मामला है, तो अशक्त लौटें। यदि फ़ंक्शन केवल एक खाली संग्रह वापस करने के लिए होता है, तो जाहिर है कि वापस आना ठीक है। हालांकि, अमान्य मापदंडों या अन्य कारणों से एक खाली संग्रह को एक विशेष मामले के रूप में वापस करना एक अच्छा विचार नहीं है, क्योंकि यह एक विशेष मामले की स्थिति को चिह्नित कर रहा है।

वास्तव में, इस मामले में मैं आमतौर पर यह सुनिश्चित करने के लिए एक अपवाद फेंकना पसंद करता हूं कि यह वास्तव में अनदेखा नहीं है :)

यह कहते हुए कि यह कोड को और अधिक मजबूत बनाता है (खाली संग्रह लौटाकर) क्योंकि उनके पास अशक्त स्थिति को संभालना नहीं है, यह खराब है, क्योंकि यह केवल एक समस्या का सामना कर रहा है जिसे कॉलिंग कोड द्वारा नियंत्रित किया जाना चाहिए।


4

मेरा तर्क है कि nullएक खाली संग्रह के रूप में एक ही बात नहीं है और आपको यह चुनना चाहिए कि कौन सा सबसे अच्छा प्रतिनिधित्व करता है जो आप लौट रहे हैं। ज्यादातर मामलों nullमें कुछ भी नहीं है (SQL को छोड़कर)। एक खाली संग्रह कुछ है, एक खाली कुछ है।

यदि आपको एक या दूसरे को चुनना है, तो मैं कहूंगा कि आपको शून्य के बजाय एक खाली संग्रह की ओर बढ़ना चाहिए। लेकिन कई बार ऐसा भी होता है कि खाली संग्रह शून्य मान के समान नहीं होता है।


4

हमेशा अपने ग्राहकों के पक्ष में सोचें (जो आपके एपीआई का उपयोग कर रहे हैं):

'अशक्त' लौटना अक्सर ग्राहकों के साथ समस्या है जो अशक्त जांच को सही तरीके से नहीं संभालता है, जो रनटाइम के दौरान NullPointerException का कारण बनता है। मैंने ऐसे मामलों को देखा है जहां इस तरह के लापता नल-जांच ने एक प्राथमिकता उत्पादन मुद्दे (एक ग्राहक ने अशक्त मूल्य पर ...) का इस्तेमाल किया। परीक्षण के दौरान समस्या उत्पन्न नहीं हुई, क्योंकि संचालित डेटा थोड़ा अलग था।


3

मैं यहां उपयुक्त उदाहरण के साथ व्याख्या देना पसंद करता हूं।

यहाँ एक मामले पर विचार करें ..

int totalValue = MySession.ListCustomerAccounts()
                          .FindAll(ac => ac.AccountHead.AccountHeadID 
                                         == accountHead.AccountHeadID)
                          .Sum(account => account.AccountValue);

यहां उन कार्यों पर विचार करें जिनका मैं उपयोग कर रहा हूं ..

1. ListCustomerAccounts() // User Defined
2. FindAll()              // Pre-defined Library Function

मैं आसानी से ListCustomerAccountऔर FindAllइसके बजाय का उपयोग कर सकते हैं ।

int totalValue = 0; 
List<CustomerAccounts> custAccounts = ListCustomerAccounts();
if(custAccounts !=null ){
  List<CustomerAccounts> custAccountsFiltered = 
        custAccounts.FindAll(ac => ac.AccountHead.AccountHeadID 
                                   == accountHead.AccountHeadID );
   if(custAccountsFiltered != null)
      totalValue = custAccountsFiltered.Sum(account => 
                                            account.AccountValue).ToString();
}

ध्यान दें: चूंकि AccountValue नहीं है null, इसलिए Sum () फ़ंक्शन वापस नहीं आएगा null। इसलिए मैं इसे सीधे उपयोग कर सकता हूं।


2

हमने एक-एक सप्ताह पहले विकास टीम के बीच यह चर्चा की थी, और हम लगभग सर्वसम्मति से खाली संग्रह के लिए गए थे। एक व्यक्ति उसी कारण से वापस लौटना चाहता था जिस कारण माइक ऊपर निर्दिष्ट किया गया था।


2

खाली संग्रह। यदि आप C # का उपयोग कर रहे हैं, तो धारणा यह है कि सिस्टम संसाधनों को अधिकतम करना आवश्यक नहीं है। कम कुशल होते हुए, खाली किए गए संग्रह को वापस करना प्रोग्रामर के लिए बहुत अधिक सुविधाजनक है (ऊपर दिए गए कारण के लिए)।


2

ज्यादातर मामलों में एक खाली संग्रह लौटना बेहतर है।

इसका कारण कॉलर के कार्यान्वयन, सुसंगत अनुबंध और आसान कार्यान्वयन की सुविधा है।

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

IEnumerable के लिए अशक्त का एक वैध उपयोग अनुपस्थित परिणाम, या एक ऑपरेशन विफलता का संकेत हो सकता है, लेकिन इस मामले में अन्य तकनीकों पर विचार किया जाना चाहिए, जैसे कि एक अपवाद फेंकना।

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
    /// <summary>
    /// Demonstrates different approaches for empty collection results.
    /// </summary>
    class Container
    {
        /// <summary>
        /// Elements list.
        /// Not initialized to an empty collection here for the purpose of demonstration of usage along with <see cref="Populate"/> method.
        /// </summary>
        private List<Element> elements;

        /// <summary>
        /// Gets elements if any
        /// </summary>
        /// <returns>Returns elements or empty collection.</returns>
        public IEnumerable<Element> GetElements()
        {
            return elements ?? Enumerable.Empty<Element>();
        }

        /// <summary>
        /// Initializes the container with some results, if any.
        /// </summary>
        public void Populate()
        {
            elements = new List<Element>();
        }

        /// <summary>
        /// Gets elements. Throws <see cref="InvalidOperationException"/> if not populated.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>.</returns>
        public IEnumerable<Element> GetElementsStrict()
        {
            if (elements == null)
            {
                throw new InvalidOperationException("You must call Populate before calling this method.");
            }

            return elements;
        }

        /// <summary>
        /// Gets elements, empty collection or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with zero or more elements, or null in some cases.</returns>
        public IEnumerable<Element> GetElementsInconvenientCareless()
        {
            return elements;
        }

        /// <summary>
        /// Gets elements or nothing.
        /// </summary>
        /// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with elements, or null in case of empty collection.</returns>
        /// <remarks>We are lucky that elements is a List, otherwise enumeration would be needed.</remarks>
        public IEnumerable<Element> GetElementsInconvenientCarefull()
        {
            if (elements == null || elements.Count == 0)
            {
                return null;
            }
            return elements;
        }
    }

    class Element
    {
    }

    /// <summary>
    /// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
    /// </summary>
    class EmptyCollectionTests
    {
        private Container container;

        [SetUp]
        public void SetUp()
        {
            container = new Container();
        }

        /// <summary>
        /// Forgiving contract - caller does not have to implement null check in addition to enumeration.
        /// </summary>
        [Test]
        public void UseGetElements()
        {
            Assert.AreEqual(0, container.GetElements().Count());
        }

        /// <summary>
        /// Forget to <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void WrongUseOfStrictContract()
        {
            container.GetElementsStrict().Count();
        }

        /// <summary>
        /// Call <see cref="Container.Populate"/> and use strict method.
        /// </summary>
        [Test]
        public void CorrectUsaOfStrictContract()
        {
            container.Populate();
            Assert.AreEqual(0, container.GetElementsStrict().Count());
        }

        /// <summary>
        /// Inconvenient contract - needs a local variable.
        /// </summary>
        [Test]
        public void CarefulUseOfCarelessMethod()
        {
            var elements = container.GetElementsInconvenientCareless();
            Assert.AreEqual(0, elements == null ? 0 : elements.Count());
        }

        /// <summary>
        /// Inconvenient contract - duplicate call in order to use in context of an single expression.
        /// </summary>
        [Test]
        public void LameCarefulUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
        }

        [Test]
        public void LuckyCarelessUseOfCarelessMethod()
        {
            // INIT
            var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
            praySomeoneCalledPopulateBefore();

            // ACT //ASSERT
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Excercise <see cref="ArgumentNullException"/> because of null passed to <see cref="Enumerable.Count{TSource}(System.Collections.Generic.IEnumerable{TSource})"/>
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void UnfortunateCarelessUseOfCarelessMethod()
        {
            Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
        }

        /// <summary>
        /// Demonstrates the client code flow relying on returning null for empty collection.
        /// Exception is due to <see cref="Enumerable.First{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> on an empty collection.
        /// </summary>
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void UnfortunateEducatedUseOfCarelessMethod()
        {
            container.Populate();
            var elements = container.GetElementsInconvenientCareless();
            if (elements == null)
            {
                Assert.Inconclusive();
            }
            Assert.IsNotNull(elements.First());
        }

        /// <summary>
        /// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
        /// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
        /// We are unfortunate to create a new instance of an empty collection.
        /// We might have already had one inside the implementation,
        /// but it have been discarded then in an effort to return null for empty collection.
        /// </summary>
        [Test]
        public void EducatedUseOfCarefullMethod()
        {
            Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty<Element>()).Count());
        }
    }
}

2

मैं इसे अपनी अरबों डॉलर की गलती कहता हूं ... उस समय, मैं ऑब्जेक्ट-ओरिएंटेड भाषा में संदर्भ के लिए पहला व्यापक प्रकार का सिस्टम डिजाइन कर रहा था। मेरा लक्ष्य यह सुनिश्चित करना था कि संकलक द्वारा स्वचालित रूप से किए गए चेकिंग के साथ सभी संदर्भों का उपयोग पूरी तरह से सुरक्षित होना चाहिए। लेकिन मैं एक अशक्त संदर्भ में डालने के प्रलोभन का विरोध नहीं कर सकता था, केवल इसलिए कि इसे लागू करना इतना आसान था। इससे असंख्य त्रुटियां, कमजोरियां और सिस्टम क्रैश हो गए हैं, जिसने पिछले चालीस वर्षों में संभवतः एक अरब डॉलर का दर्द और क्षति पहुंचाई है। - टोनी होरे, ALGOL W के आविष्कारक।

सामान्य रूप से विस्तृत गंदगी के तूफान के लिए यहां देखें null। मैं उस कथन से सहमत नहीं हूं जो undefinedएक और है null, लेकिन यह अभी भी पढ़ने लायक है। और यह बताता है, कि आपको nullउस स्थिति में क्यों नहीं बचना चाहिए, न कि आपके द्वारा पूछे गए मामले में। सार यह है, कि nullकिसी भी भाषा में एक विशेष मामला है। आपको nullएक अपवाद के रूप में सोचना होगा । undefinedउस तरह से अलग है, अपरिभाषित व्यवहार से निपटने वाला कोड ज्यादातर मामलों में एक बग है। सी और अधिकांश अन्य भाषाओं में भी अपरिभाषित व्यवहार होता है लेकिन उनमें से अधिकांश के पास भाषा के लिए कोई पहचानकर्ता नहीं है।


1

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

(यह एक इकाई परीक्षण बोझ से मेल खाती है। आपको खाली संग्रह वापसी मामले के अलावा नल रिटर्न केस के लिए एक परीक्षण लिखना होगा।)

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