2008-12-19 15 views
28

क्या रिटर्न प्रकार के रूप में IEnumerable<T> का उपयोग करने में कोई समस्या है? FxCop List<T> लौटने के बारे में शिकायत करता है (यह इसके बजाय Collection<T> लौटने की सलाह देता है)।IENumerable <T> वापसी प्रकार

ठीक है, मुझे हमेशा एक नियम द्वारा निर्देशित किया गया है "कम से कम आप स्वीकार कर सकते हैं, लेकिन अधिकतम लौटाएं।"

इस बिंदु से, IEnumerable<T> लौटने पर एक बुरी बात है, लेकिन जब मैं "आलसी पुनर्प्राप्ति" का उपयोग करना चाहता हूं तो मुझे क्या करना चाहिए? इसके अलावा, yield कीवर्ड इतना अच्छा है।

उत्तर

27

यह वास्तव में दो भाग का प्रश्न है।

1) वहाँ स्वाभाविक कुछ भी एक IEnumerable < टी >

कोई कुछ भी नहीं लौटने के साथ गलत है। वास्तव में यदि आप सी # इटरेटर्स का उपयोग कर रहे हैं तो यह अपेक्षित व्यवहार है। इसे एक सूची में परिवर्तित करना < टी > या कोई अन्य संग्रह वर्ग पूर्व-स्पष्ट रूप से एक अच्छा विचार नहीं है। ऐसा करने से आपके कॉलर द्वारा उपयोग पैटर्न पर धारणा हो रही है। मुझे लगता है कि कॉलर के बारे में कुछ भी मानना ​​अच्छा नहीं है। उनके पास अच्छे कारण हो सकते हैं कि वे एक आईनेमेरेबल < टी > क्यों चाहते हैं। शायद वे इसे एक पूरी तरह से अलग संग्रह पदानुक्रम में परिवर्तित करना चाहते हैं (जिस स्थिति में सूची में रूपांतरण बर्बाद हो जाता है)।

2) क्या ऐसी कोई परिस्थितियां हैं जहां आईन्यूमेरेबल < टी > के अलावा कुछ और लौटने के लिए बेहतर हो सकता है?

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

यह निश्चित रूप से दुर्लभ मामला है।

+0

ओपी इंगित करता है कि, यदि एक ठोस वर्ग को वापस करने की आवश्यकता है, तो FxCop 'सूची ' की बजाय 'संग्रह ' को वापस करने की सलाह देता है। यह भी देखें [संग्रह बनाम सूची आप अपने इंटरफेस पर क्या उपयोग करना चाहिए?] (Http://stackoverflow.com/q/271710/1497596)। – DavidRR

27

नहीं, IEnumerable<T>अच्छा चीज़ वापस लौटने की बात है, क्योंकि आप जो भी वादा कर रहे हैं वह "टाइप (मानित) मानों का अनुक्रम है"। LINQ आदि के लिए आदर्श, और पूरी तरह से उपयोग करने योग्य।

कॉलर आसानी से इस डेटा को एक सूची (या जो कुछ भी) में डाल सकता है - खासकर LINQ (ToList, ToArray, आदि) के साथ।

यह दृष्टिकोण आपको सभी डेटा बफर करने के बजाय आलसी मूल्यों को वापस स्पूल करने की अनुमति देता है। निश्चित रूप से एक गुडिया। मैंने दूसरे दिन भी another useful IEnumerable<T> trick लिखा था।

+1

कोई विचार क्यों fxcop शिकायत करता है? – annakata

+0

क्या यह IENumerable लौटने के बारे में शिकायत करता है? यदि ऐसा है, तो इसे नमक के चुटकी से लें ... यह हमेशा सही नहीं होता है। –

+3

यदि यह आपको * वास्तव में * वापस लौटने के बारे में सोचने के लिए प्रेरित करता है, तो fxcop ने अपना काम किया है ;- –

4

आईनेमेरेबल मेरे द्वारा ठीक है लेकिन इसमें कुछ कमीएं हैं। परिणाम प्राप्त करने के लिए ग्राहक को गणना करना है। इसके पास गणना आदि की जांच करने का कोई तरीका नहीं है सूची खराब है क्योंकि आप अधिक नियंत्रण का पर्दाफाश करते हैं; ग्राहक इससे इत्यादि जोड़/हटा सकता है और यह एक बुरी चीज हो सकती है। संग्रह कम से कम FxCop के दृश्य में सबसे अच्छा समझौता लगता है। मैं हमेशा अपने संदर्भ में एप्राइपियेट का उपयोग करता हूं (उदाहरण के लिए। अगर मैं केवल पढ़ने के लिए संग्रह को वापस लौटना चाहता हूं तो मैं संग्रह प्रकार को वापस लौटाता हूं और वापसी सूची के रूप में खुलासा करता हूं। केवल पढ़ने के लिए() या उपज आदि के माध्यम से आलसी मूल्यांकन के लिए IENumerable)। मामले के आधार पर इसे

+1

देता है क्लाइंट ToList(), ToArray(), गणना() या जो कुछ भी चाहता है उसका उपयोग कर सकता है। –

+1

लेकिन वे विधियां नई ऑब्जेक्ट्स बनाती हैं। किए गए किसी भी संशोधन मूल सूची वस्तु –

+0

अच्छा बिंदु पर प्रतिबिंबित नहीं करते हैं! केस-दर-मामले आधार सबसे अच्छा लगता है। हालांकि मैं आईनेमरेबल दृष्टिकोण पसंद करता हूं, यह अपरिवर्तनीयता का पक्ष लेता है। –

1

बस क्योंकि आप कहते हैं कि आप IENumerable लौट रहे हैं इसका मतलब यह नहीं है कि आप एक सूची वापस नहीं कर सकते हैं। विचार अनियंत्रित युग्मन को कम करना है। कॉलर को उस सूची को रखने के लिए उपयोग किए जाने वाले सटीक प्रकार के संग्रह की बजाय चीज़ों की एक सूची प्राप्त करना चाहिए। यदि आपके पास ऐसा कुछ है जो किसी सरणी द्वारा समर्थित है, तो गणना की तरह कुछ प्राप्त करना वैसे भी तेज़ होगा।

+3

यदि कॉलर * सूची * की अपेक्षा करता है, तो IList [] एक अच्छा विकल्प होगा। –

0

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

2

रिटर्निंग IENumerable < टी > ठीक है अगर आप वास्तव में केवल एक गणना वापस कर रहे हैं, और यह आपके कॉलर द्वारा इस तरह से उपभोग किया जाएगा।

लेकिन जैसा कि अन्य इंगित करते हैं, इस बात की कमी है कि कॉलर को किसी अन्य जानकारी की आवश्यकता होने पर गणना करने की आवश्यकता हो सकती है (उदाहरण के लिए गणना)। .NET 3.5 एक्सटेंशन विधि IENumerable < टी > काउंटर दृश्यों के पीछे गणना करेंगे यदि वापसी मान आईसीओलेक्शन < टी > लागू नहीं करता है, जो अवांछित हो सकता है।

मैं अक्सर IList < टी > या ICollection < टी > लौट जब परिणाम एक संग्रह है - आंतरिक रूप से अपने विधि एक सूची < टी > और या तो इसे वापस के रूप में है, या वापसी सूची < टी > .AsReadOnly अगर उपयोग कर सकते हैं आप संशोधन के खिलाफ सुरक्षा करना चाहते हैं (उदाहरण के लिए यदि आप आंतरिक रूप से सूची को कैश कर रहे हैं)। AFAIK FxCop इनमें से किसी के साथ काफी खुश है।

3

अपने सिद्धांत के बारे में: "कम से कम स्वीकार करें, लेकिन अधिकतम लौटाएं"।

एक बड़े कार्यक्रम की जटिलता को प्रबंधित करने की कुंजी जानकारी छिपाने वाली तकनीक है। यदि आपकी विधि List<T> बनाकर काम करती है, तो इस प्रकार को वापस लौटकर इस तथ्य को प्रकट करना अक्सर आवश्यक नहीं होता है। यदि आप करते हैं, तो आपके कॉलर वापस आने वाली सूची को संशोधित कर सकते हैं। यह yield return के साथ कैशिंग, या आलसी पुनरावृत्ति करने की आपकी क्षमता को हटा देता है।

तो फ़ंक्शन का पालन करने के लिए एक बेहतर सिद्धांत यह है: "आप कैसे काम करते हैं इसके बारे में जितना संभव हो उतना प्रकट करें"।

2

एक महत्वपूर्ण पहलू यह है कि जब आप List<T> वापस करते हैं तो आप वास्तविक रूप से संदर्भ लौट रहे हैं। इससे कॉलर के लिए आपकी सूची में हेरफेर करना संभव हो जाता है। यह एक आम समस्या है-उदाहरण के लिए, एक व्यापार परत जो एक जीयूआई परत पर List<T> लौटाती है।

3

"कम से कम स्वीकार करें, लेकिन अधिकतम लौटाएं" जो मैं वकालत करता हूं। जब कोई विधि किसी वस्तु को वापस लाती है, तो हमें किस प्रकार की औचित्य को वास्तविक प्रकार वापस नहीं करना है और मूल प्रकार को वापस कर ऑब्जेक्ट की क्षमताओं को सीमित करना है। हालांकि यह एक प्रश्न उठाता है कि हम कैसे जानते हैं कि "अधिकतम" (वास्तविक प्रकार) क्या होगा जब हम एक इंटरफ़ेस डिज़ाइन करेंगे। जवाब बहुत आसान है। केवल चरम मामलों में जहां इंटरफेस डिजाइनर एक खुले इंटरफेस को डिजाइन कर रहा है, जिसे एप्लिकेशन/घटक के बाहर लागू किया जाएगा, वे नहीं जानते कि वास्तविक रिटर्न प्रकार क्या हो सकता है। एक स्मार्ट डिजाइनर को हमेशा यह विचार करना चाहिए कि विधि क्या करनी चाहिए और क्या इष्टतम/जेनेरिक रिटर्न प्रकार होना चाहिए।

उदा। यदि मैं ऑब्जेक्ट्स के वेक्टर को पुनर्प्राप्त करने के लिए एक इंटरफ़ेस डिज़ाइन कर रहा हूं, और मुझे पता है कि लौटाई गई वस्तुओं की गिनती चरणीय होने जा रही है, तो मैं हमेशा एक स्मार्ट डेवलपर हमेशा एक सूची का उपयोग करूंगा। अगर कोई ऐरे वापस करने की योजना बना रहा है, तो मैं उसकी क्षमताओं पर सवाल उठाऊंगा, जब तक कि वह सिर्फ उस परत से डेटा वापस नहीं कर लेता है जिसके पास उसका स्वामित्व नहीं है। और शायद यही कारण है कि FxCop ICollection (सूची और ऐरे के लिए सामान्य आधार) के लिए वकालत करता है।

उक्त जा रहा है ऊपर, वहाँ अन्य चीजों की जोड़ी पर विचार करने के

  • यदि दिए गए डेटा परिवर्तनशील या अपरिवर्तनीय

  • होना चाहिए दिए गए डेटा से अधिक कॉल करने में साझा किया है, तो कर रहे हैं

LINQ आलसी मूल्यांकन के संबंध में मुझे यकीन है कि 95% + सी # उपयोगकर्ता आंतों को नहीं समझते हैं। यह इतना गैर-ओह-आश है। ओओ विधि आमंत्रण पर ठोस राज्य परिवर्तन को बढ़ावा देता है। LINQ आलसी मूल्यांकन अभिव्यक्ति मूल्यांकन पैटर्न पर रनटाइम राज्य परिवर्तन को बढ़ावा देता है (कुछ गैर-उन्नत उपयोगकर्ता हमेशा पालन नहीं करते हैं)।

0

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

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

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

संबंधित मुद्दे