यह एक सामान्य प्रश्न की तरह है (लेकिन मैं C # का उपयोग कर रहा हूं), सबसे अच्छा तरीका क्या है (सबसे अच्छा अभ्यास), क्या आप एक विधि के लिए अशक्त या खाली संग्रह लौटाते हैं जिसमें एक वापसी प्रकार के रूप में एक संग्रह है?
यह एक सामान्य प्रश्न की तरह है (लेकिन मैं C # का उपयोग कर रहा हूं), सबसे अच्छा तरीका क्या है (सबसे अच्छा अभ्यास), क्या आप एक विधि के लिए अशक्त या खाली संग्रह लौटाते हैं जिसमें एक वापसी प्रकार के रूप में एक संग्रह है?
जवाबों:
खाली संग्रह। हमेशा।
यह बेकार है:
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>()
को लौटने की तुलना में अधिक कुशल के रूप में देखा जा सकता है, उदाहरण के लिए, एक नया खाली संग्रह या सरणी।
IEnumerable
या ICollection
नहीं। वैसे भी, यदि आप कुछ प्रकार का चयन करते हैं तो ICollection
वे भी लौट जाते हैं null
... मैं चाहूंगा कि वे एक खाली संग्रह लौटाएं, लेकिन मैं उन्हें लौटाने में भाग गया null
, इसलिए मैंने सोचा कि मैं यहां इसका उल्लेख करूंगा। मैं कहूंगा कि असंख्य संग्रह का डिफ़ॉल्ट खाली नहीं है। मुझे नहीं पता था कि यह इतना संवेदनशील विषय था।
से फ्रेमवर्क डिजाइन दिशानिर्देश 2 संस्करण (पृष्ठ 256।):
संग्रह गुणों से या संग्रह रिटर्न करने के तरीकों से शून्य मान वापस न करें। इसके बजाय एक खाली संग्रह या एक खाली सरणी लौटें।
यहाँ एक और दिलचस्प लेख है, अशक्त न लौटने के लाभों पर (मैं ब्रैड अब्राम के ब्लॉग पर कुछ खोजने की कोशिश कर रहा था, और वह लेख से जुड़ा हुआ है)।
संपादित करें- जैसा कि एरिक लिपर्ट ने अब मूल प्रश्न पर टिप्पणी की है, मैं उनके उत्कृष्ट लेख से लिंक करना चाहूंगा ।
आपके अनुबंध और आपके ठोस मामले पर निर्भर करता है । आम तौर पर खाली संग्रह वापस करना सबसे अच्छा है , लेकिन कभी-कभी ( शायद ही कभी ):
null
कुछ अधिक विशिष्ट मतलब हो सकता है;null
।कुछ ठोस उदाहरण:
null
इसका अर्थ होगा कि तत्व गायब है, जबकि एक खाली संग्रह एक निरर्थक (और संभवतः गलत) को प्रस्तुत करेगा।<collection />
एक अन्य बिंदु है जिसका अभी तक उल्लेख नहीं किया गया है। निम्नलिखित कोड पर विचार करें:
public static IEnumerable<string> GetFavoriteEmoSongs()
{
yield break;
}
इस पद्धति को कॉल करने पर C # भाषा एक खाली एन्यूमरेटर लौटाएगी। इसलिए, भाषा के डिजाइन के अनुरूप (और, इस प्रकार, प्रोग्रामर अपेक्षाएं) एक खाली संग्रह लौटाया जाना चाहिए।
खाली अधिक उपभोक्ता के अनुकूल है।
खाली गणना करने की एक स्पष्ट विधि है:
Enumerable.Empty<Element>()
यह मुझे प्रतीत होता है कि आपको उस मूल्य को वापस करना चाहिए जो कि संदर्भ में शब्दार्थ रूप से सही है, जो कुछ भी हो सकता है। एक नियम जो कहता है "हमेशा एक खाली संग्रह लौटाएं" मेरे लिए थोड़ा सरल है।
मान लीजिए, कहते हैं, एक अस्पताल के लिए एक प्रणाली, हमारे पास एक फ़ंक्शन है जो पिछले 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 से अपरिचित है, जब वह बिना किसी डेटा के परीक्षण करता है, तो वह जल्दी से यह पता लगा लेगा कि कभी-कभी वह वापस शून्य हो जाता है। लेकिन वह किसी अन्य फ़ंक्शन के अस्तित्व की खोज कैसे करेगा जब तक कि उसके साथ ऐसा न हो कि ऐसा कोई फ़ंक्शन हो सकता है और वह दस्तावेज़ीकरण की जांच करता है, और दस्तावेज़ पूर्ण और समझ में आता है? मेरे पास एक ऐसा कार्य होगा जो हमेशा मुझे दो कार्यों के बजाय एक सार्थक प्रतिक्रिया देता है, जिसे मुझे दोनों को कॉल करने के लिए जानना और याद रखना है।
अगर एक खाली संग्रह अर्थपूर्ण रूप से समझ में आता है, तो यही है कि मैं वापसी करना पसंद करता हूं। GetMessagesInMyInbox()
संचार के लिए एक खाली संग्रह लौटना "आपके इनबॉक्स में वास्तव में कोई संदेश नहीं है", जबकि लौटने से null
यह संप्रेषित करने के लिए उपयोगी हो सकता है कि अपर्याप्त डेटा यह कहने के लिए उपलब्ध है कि जिस सूची को लौटाया जाना चाहिए, वह देखने के लिए चाहिए।
null
मूल्य निश्चित रूप से उचित नहीं लगता है, मैं उस पर अधिक सामान्य शब्दों में सोच रहा था। अपवाद इस तथ्य को संप्रेषित करने के लिए भी महान हैं कि कुछ गलत हो गया है, लेकिन यदि "अपर्याप्त डेटा" का उल्लेख पूरी तरह से अपेक्षित है, तो एक अपवाद फेंकने से खराब डिजाइन होगा। मैं इसके बजाय एक ऐसे परिदृश्य के बारे में सोच रहा हूं जहां यह पूरी तरह से संभव है और इस पद्धति के लिए कोई भी त्रुटि कभी-कभी प्रतिक्रिया की गणना करने में सक्षम नहीं होती है।
वापसी करने वाला अधिक कुशल हो सकता है, क्योंकि कोई नई वस्तु नहीं बनाई गई है। हालाँकि, इसमें अक्सर null
चेक (या अपवाद हैंडलिंग) की भी आवश्यकता होती है ।
शब्दार्थ, null
और एक खाली सूची का मतलब एक ही बात नहीं है। अंतर सूक्ष्म हैं और एक उदाहरण विशिष्ट उदाहरणों में अन्य की तुलना में बेहतर हो सकता है।
अपनी पसंद के बावजूद, भ्रम से बचने के लिए इसे दस्तावेज़ करें।
कोई यह तर्क दे सकता है कि शून्य ऑब्जेक्ट पैटर्न के पीछे तर्क खाली संग्रह को वापस करने के पक्ष में एक के समान है।
हालात के उपर निर्भर। यदि यह एक विशेष मामला है, तो अशक्त लौटें। यदि फ़ंक्शन केवल एक खाली संग्रह वापस करने के लिए होता है, तो जाहिर है कि वापस आना ठीक है। हालांकि, अमान्य मापदंडों या अन्य कारणों से एक खाली संग्रह को एक विशेष मामले के रूप में वापस करना एक अच्छा विचार नहीं है, क्योंकि यह एक विशेष मामले की स्थिति को चिह्नित कर रहा है।
वास्तव में, इस मामले में मैं आमतौर पर यह सुनिश्चित करने के लिए एक अपवाद फेंकना पसंद करता हूं कि यह वास्तव में अनदेखा नहीं है :)
यह कहते हुए कि यह कोड को और अधिक मजबूत बनाता है (खाली संग्रह लौटाकर) क्योंकि उनके पास अशक्त स्थिति को संभालना नहीं है, यह खराब है, क्योंकि यह केवल एक समस्या का सामना कर रहा है जिसे कॉलिंग कोड द्वारा नियंत्रित किया जाना चाहिए।
मेरा तर्क है कि null
एक खाली संग्रह के रूप में एक ही बात नहीं है और आपको यह चुनना चाहिए कि कौन सा सबसे अच्छा प्रतिनिधित्व करता है जो आप लौट रहे हैं। ज्यादातर मामलों null
में कुछ भी नहीं है (SQL को छोड़कर)। एक खाली संग्रह कुछ है, एक खाली कुछ है।
यदि आपको एक या दूसरे को चुनना है, तो मैं कहूंगा कि आपको शून्य के बजाय एक खाली संग्रह की ओर बढ़ना चाहिए। लेकिन कई बार ऐसा भी होता है कि खाली संग्रह शून्य मान के समान नहीं होता है।
हमेशा अपने ग्राहकों के पक्ष में सोचें (जो आपके एपीआई का उपयोग कर रहे हैं):
'अशक्त' लौटना अक्सर ग्राहकों के साथ समस्या है जो अशक्त जांच को सही तरीके से नहीं संभालता है, जो रनटाइम के दौरान NullPointerException का कारण बनता है। मैंने ऐसे मामलों को देखा है जहां इस तरह के लापता नल-जांच ने एक प्राथमिकता उत्पादन मुद्दे (एक ग्राहक ने अशक्त मूल्य पर ...) का इस्तेमाल किया। परीक्षण के दौरान समस्या उत्पन्न नहीं हुई, क्योंकि संचालित डेटा थोड़ा अलग था।
मैं यहां उपयुक्त उदाहरण के साथ व्याख्या देना पसंद करता हूं।
यहाँ एक मामले पर विचार करें ..
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
। इसलिए मैं इसे सीधे उपयोग कर सकता हूं।
ज्यादातर मामलों में एक खाली संग्रह लौटना बेहतर है।
इसका कारण कॉलर के कार्यान्वयन, सुसंगत अनुबंध और आसान कार्यान्वयन की सुविधा है।
यदि कोई विधि रिक्त परिणाम को इंगित करने के लिए अशक्त हो जाती है, तो कॉल करने वाले को एन्यूमरेशन के अलावा एक अशक्त जाँच एडाप्टर को लागू करना चाहिए। इस कोड को फिर विभिन्न कॉलर्स में डुप्लिकेट किया गया है, इसलिए इस एडाप्टर को विधि के अंदर क्यों नहीं रखा जाए ताकि इसका पुन: उपयोग किया जा सके।
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());
}
}
}
मैं इसे अपनी अरबों डॉलर की गलती कहता हूं ... उस समय, मैं ऑब्जेक्ट-ओरिएंटेड भाषा में संदर्भ के लिए पहला व्यापक प्रकार का सिस्टम डिजाइन कर रहा था। मेरा लक्ष्य यह सुनिश्चित करना था कि संकलक द्वारा स्वचालित रूप से किए गए चेकिंग के साथ सभी संदर्भों का उपयोग पूरी तरह से सुरक्षित होना चाहिए। लेकिन मैं एक अशक्त संदर्भ में डालने के प्रलोभन का विरोध नहीं कर सकता था, केवल इसलिए कि इसे लागू करना इतना आसान था। इससे असंख्य त्रुटियां, कमजोरियां और सिस्टम क्रैश हो गए हैं, जिसने पिछले चालीस वर्षों में संभवतः एक अरब डॉलर का दर्द और क्षति पहुंचाई है। - टोनी होरे, ALGOL W के आविष्कारक।
सामान्य रूप से विस्तृत गंदगी के तूफान के लिए यहां देखें null
। मैं उस कथन से सहमत नहीं हूं जो undefined
एक और है null
, लेकिन यह अभी भी पढ़ने लायक है। और यह बताता है, कि आपको null
उस स्थिति में क्यों नहीं बचना चाहिए, न कि आपके द्वारा पूछे गए मामले में। सार यह है, कि null
किसी भी भाषा में एक विशेष मामला है। आपको null
एक अपवाद के रूप में सोचना होगा । undefined
उस तरह से अलग है, अपरिभाषित व्यवहार से निपटने वाला कोड ज्यादातर मामलों में एक बग है। सी और अधिकांश अन्य भाषाओं में भी अपरिभाषित व्यवहार होता है लेकिन उनमें से अधिकांश के पास भाषा के लिए कोई पहचानकर्ता नहीं है।
एक प्राथमिक सॉफ्टवेयर इंजीनियरिंग उद्देश्य के प्रबंधन की जटिलता के परिप्रेक्ष्य से, हम अनावश्यक प्रचार से बचना चाहते हैं एक एपीआई के ग्राहकों को चक्रीय जटिलता का । ग्राहक के लिए एक अशक्त लौटना उन्हें एक और कोड शाखा की चक्रीय जटिलता लागत वापस करने जैसा है।
(यह एक इकाई परीक्षण बोझ से मेल खाती है। आपको खाली संग्रह वापसी मामले के अलावा नल रिटर्न केस के लिए एक परीक्षण लिखना होगा।)