क्या मुझे हमेशा ILI <T> के बजाय IEnumerable <T> वापस करना चाहिए?


98

जब मैं अपना DAL या अन्य कोड लिख रहा हूं जो आइटमों का एक सेट लौटाता है, तो क्या मुझे हमेशा अपना रिटर्न स्टेटमेंट बनाना चाहिए:

public IEnumerable<FooBar> GetRecentItems()

या

public IList<FooBar> GetRecentItems()

वर्तमान में, मेरे कोड में मैं संभव के रूप में IEnumerable का उपयोग करने की कोशिश कर रहा हूँ, लेकिन मुझे यकीन नहीं है कि यह सबसे अच्छा अभ्यास है? यह सही लग रहा था क्योंकि मैं अभी भी सबसे सामान्य डेटाटाइप वापस कर रहा था जबकि यह क्या करता है, इसके बारे में वर्णनात्मक है, लेकिन शायद यह करने के लिए सही नहीं है।


1
क्या यह सूची <T> या IList <T> आप के बारे में पूछ रहे हैं? शीर्षक और सवाल अलग-अलग बातें कहते हैं ...
फ्रेड्रिक मोर्क

जब कभी भी संभव हो यूजर इंटरफेस IEnumerable या संक्षिप्त सूची प्रकार के Ilist इंस्टा।
उस्मान मसूद

2
मैं सूची <T> के रूप में संग्रह लौटाता हूं। मुझे IEnumberable <T> को वापस करने की आवश्यकता नहीं है क्योंकि आप सूची <T> से निकाल सकते हैं
चक कॉनवे


जवाबों:


45

यह वास्तव में इस बात पर निर्भर करता है कि आप उस विशिष्ट इंटरफ़ेस का उपयोग क्यों कर रहे हैं।

उदाहरण के लिए, IList<T>कई तरीके हैं जो इसमें मौजूद नहीं हैं IEnumerable<T>:

  • IndexOf(T item)
  • Insert(int index, T item)
  • RemoveAt(int index)

और गुण:

  • T this[int index] { get; set; }

यदि आपको किसी भी तरह से इन तरीकों की आवश्यकता है, तो सभी तरीकों से वापस आ सकते हैं IList<T>

इसके अलावा, यदि विधि जो आपके IEnumerable<T>परिणाम का उपभोग करती है IList<T>, वह उम्मीद कर रही है , तो यह CLR को किसी भी रूपांतरण की आवश्यकता पर विचार करने से बचाएगा, इस प्रकार संकलित कोड का अनुकूलन।


2
@Jon FDG संग्रह <T> या ReadOnlyCollection <T> का उपयोग करने की सलाह देते हैं, संग्रह प्रकारों के लिए वापसी मूल्य मेरे उत्तर को देखें।
सैम केसर

जब आप कहते हैं कि आप स्पष्ट नहीं हैं कि आपके अंतिम वाक्य का क्या मतलब है "तो यह सीएलआर को बचाएगा"। IEnumerable बनाम IList का उपयोग करके इसे क्या बचाएगा? क्या आप इसे स्पष्ट कर सकते हैं?
पॉजिटिव

1
@ कॉफ़ी एडिक्ट इस उत्तर के तीन साल बाद मुझे लगता है कि आप सही हैं - वह अंतिम भाग अस्पष्ट है। यदि एक विधि जो एक पैरामीटर के रूप में IList <T> की अपेक्षा करती है, तो एक IEnumerable <T> प्राप्त होता है, IEnumerable को नई सूची <T> या अन्य IList <T> कार्यान्वयनकर्ता में मैन्युअल रूप से लपेटना पड़ता है, और यह कार्य उसके द्वारा नहीं किया जाएगा आपके लिए सीएलआर। इसके विपरीत - IEnumerable <T> को IList <T> प्राप्त करने की अपेक्षा करने वाला एक तरीका, कुछ अनबॉक्सिंग करना पड़ सकता है लेकिन hindight पर होने की आवश्यकता नहीं हो सकती क्योंकि IList <T> IEnumerable <> को लागू करता है।
जॉन लिमजप

69

फ्रेमवर्क डिज़ाइन दिशानिर्देश वर्ग संग्रह का उपयोग करने की सलाह देते हैं जब आपको एक संग्रह वापस करने की आवश्यकता होती है जो केवल संग्रह पढ़ने के लिए कॉलर या ReadOnlyCollection द्वारा संशोधित होता है ।

यह एक सरल के लिए पसंद किया IListजाता है क्योंकि IListयह कॉल करने वाले को सूचित नहीं करता है कि क्या यह केवल पढ़ा जाता है या नहीं।

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

ध्यान रखें कि LINQ अपनी आस्तीन ऊपर कुछ चाल है और वे पर प्रदर्शन कर रहे हैं के आधार पर कुछ कॉल का अनुकूलन करेगा। इसलिए, उदाहरण के लिए, यदि आप एक प्रदर्शन करते हैं Countऔर अंतर्निहित संग्रह एक सूची है तो यह सभी तत्वों के माध्यम से नहीं चलेगा।

निजी तौर पर, एक ORM के लिए मैं शायद Collection<T>अपनी वापसी मूल्य के साथ रहना होगा ।


14
संग्रह के लिए दिशानिर्देश डॉस और donts के एक अधिक विस्तृत सूची शामिल।
चाकोतय

27

सामान्य तौर पर, आपको सबसे सामान्य चाहिए और सबसे विशिष्ट चीज़ को वापस करना चाहिए जो आप कर सकते हैं। इसलिए यदि आपके पास एक ऐसा तरीका है जो एक पैरामीटर लेता है, और आपको केवल वास्तव में IEnumerable में उपलब्ध है की आवश्यकता है, तो वह आपका पैरामीटर प्रकार होना चाहिए। यदि आपकी विधि या तो IList या IEnumerable वापस आ सकती है, तो IList वापस करना पसंद करें। यह सुनिश्चित करता है कि यह उपभोक्ताओं की सबसे विस्तृत श्रृंखला द्वारा उपयोग करने योग्य है।

आपको जो चाहिए उसमें ढीला हो और जो आप प्रदान करते हैं उसमें स्पष्ट हो।


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

3
सामान्य प्रकारों को इनपुट के रूप में स्वीकार करने का औचित्य यह है कि यह आपको इनपुट की व्यापक श्रेणी के साथ काम करने की अनुमति देता है ताकि एक घटक से अधिक से अधिक पुन: उपयोग हो सके। दूसरी ओर, चूंकि आप पहले से ही जानते हैं कि आपके पास किस तरह का ऑब्जेक्ट है, इसलिए इसे मास्क करने में बहुत ज्यादा फायदा नहीं है।
मेल

1
मुझे लगता है कि मैं अधिक सामान्य मापदंडों से सहमत हूं, लेकिन कुछ कम सामान्य रिटर्न के लिए आपका तर्क क्या है?
डेव कज़िन्यू

7
ठीक है, मुझे यह एक और तरीका आज़माने दें। आप जानकारी क्यों फेंक देंगे? यदि आप केवल IEnumerable <T> होने के परिणाम के बारे में परवाह करते हैं, तो क्या यह आपको किसी भी तरह से यह जानने के लिए चोट पहुँचाता है कि यह IList <T> है? नहीं, यह नहीं है। यह कुछ मामलों में बेहतर जानकारी हो सकती है, लेकिन इससे आपको कोई नुकसान नहीं होता है। अब लाभ के लिए। यदि आप एक सूची या IList लौटाते हैं, तो मैं तुरंत बता सकता हूं कि संग्रह पहले ही पुनर्प्राप्त कर लिया गया है, कुछ जिसे मैं IEnumable के साथ नहीं जान सकता। यह उपयोगी जानकारी हो सकती है या नहीं, लेकिन एक बार फिर, आप जानकारी क्यों फेंक देंगे? यदि आप किसी चीज़ के बारे में अतिरिक्त जानकारी जानते हैं, तो उसे पास करें।
मेल 13

पूर्वव्यापी में, मुझे आश्चर्य है कि किसी ने भी मुझे उस एक पर नहीं बुलाया। एक IEnumerable WOULD को पहले ही पुनः प्राप्त कर लिया गया है। हालाँकि, एक आईनेजेबल को एक असंबद्ध स्थिति में वापस किया जा सकता है। तो शायद एक बेहतर उदाहरण IQueryable बनाम IEnumerable वापस आ जाएगा। अधिक विशिष्ट IQueryable को लौटाने से आगे की रचना के लिए अनुमति मिलेगी, जबकि IEnumerable को वापस करने से तत्काल गणना को बल मिलेगा, और विधि की संयोजकता में कमी आएगी।
मेल

23

वह निर्भर करता है...

IEnumerableट्रैक के नीचे अंतर्निहित कार्यान्वयन को बदलने के लिए सबसे कम व्युत्पन्न प्रकार ( ) को वापस करने से आप सबसे अधिक मार्ग छोड़ देंगे।

अधिक व्युत्पन्न प्रकार ( IList) लौटाने से आपके एपीआई के उपयोगकर्ता परिणाम पर अधिक परिचालन प्रदान करते हैं।

मैं हमेशा सबसे कम व्युत्पन्न प्रकार को वापस करने का सुझाव दूंगा जिसमें आपके उपयोगकर्ता के लिए आवश्यक सभी ऑपरेशन हैं ... इसलिए मूल रूप से, आपको सबसे पहले यह प्राप्त करना होगा कि परिणाम पर कौन से संचालन आपके द्वारा परिभाषित एपीआई के संदर्भ में समझ में आता है।


अच्छा सामान्य उत्तर क्योंकि यह अन्य तरीकों पर भी लागू होता है।
194 बजे user420667

1
मुझे पता है कि यह उत्तर बहुत पुराना है ... लेकिन यह प्रलेखन के विरोधाभासी लगता है: "यदि न तो IDEDIA <TKey, TValue> इंटरफ़ेस और न ही IList <T> इंटरफ़ेस आवश्यक संग्रह की आवश्यकताओं को पूरा करता है, तो नया संग्रह वर्ग प्राप्त करें ICollection <T> अधिक लचीलेपन के बजाय इंटरफ़ेस "यह प्रतीत होता है कि अधिक व्युत्पन्न प्रकार को प्राथमिकता दी जानी चाहिए। ( Msdn.microsoft.com/en-us/library/92t2ye13(v=vs.110).aspx ) \
देबोराह

11

विचार करने के लिए एक बात यह है कि यदि आप अपना जनरेट करने के लिए एक डिफ्रेंट-एक्ज़ीक्यूशन लाइनक्यू स्टेटमेंट का उपयोग कर रहे हैं, तो आपको अपने तरीके से लौटने से पहले IEnumerable<T>कॉल .ToList()करने का मतलब है कि आपके आइटम को दो बार iterated किया जा सकता है - एक बार सूची बनाने के लिए, और एक बार जब कॉलर के माध्यम से लूप्स , फ़िल्टर, या आपके रिटर्न मान को रूपांतरित करता है। व्यावहारिक होने पर, मैं LINQ-to-Objects के परिणामों को एक ठोस सूची या शब्दकोश में बदलने से बचना पसंद करता हूं जब तक कि मुझे नहीं करना है। यदि मेरे कॉलर को एक सूची की आवश्यकता है, तो यह एक आसान तरीका है - मुझे उनके लिए यह निर्णय लेने की आवश्यकता नहीं है, और यह मेरे कोड को उन मामलों में थोड़ा अधिक कुशल बनाता है, जहां कॉल करने वाला केवल एक फॉरचेक कर रहा है।


@ जोएल म्यूलर, मैं आमतौर पर उन पर वैसे भी ToList () कहता हूँ। मैं आमतौर पर अपनी बाकी परियोजनाओं के लिए IQueryable को उजागर करना पसंद नहीं करता।
KingNestor

4
मैं LINQ-to-Objects का अधिक उल्लेख कर रहा था, जहां IQueryable आमतौर पर चित्र में प्रवेश नहीं करता है। जब कोई डेटाबेस शामिल होता है, तो ToList () अधिक आवश्यक हो जाता है, क्योंकि अन्यथा आप पुनरावृति से पहले अपने कनेक्शन को बंद करने का जोखिम उठाते हैं, जो बहुत अच्छी तरह से काम नहीं करता है। हालाँकि, जब यह कोई समस्या नहीं है, तो IQueryable को बेनकाब करना बहुत आसान है, जब आप IQueryable को छिपाना चाहते हैं, तो अतिरिक्त पुनरावृत्ति के लिए मजबूर किए बिना एक IEnumerable के रूप में।
जोएल म्यूलर

9

List<T>कॉलिंग कोड को कई और सुविधाएँ प्रदान करता है, जैसे कि लौटी हुई वस्तु को संशोधित करना और सूचकांक द्वारा पहुंच। तो यह प्रश्न इस पर उबलता है: आपके आवेदन के विशिष्ट उपयोग के मामले में, क्या आप इस तरह के उपयोगों का समर्थन करना चाहते हैं (संभवतः एक ताजा निर्मित संग्रह लौटाकर!), फोन करने वाले की सुविधा के लिए - या क्या आप साधारण मामले के लिए गति चाहते हैं जब सभी! कॉल करने वाले की ज़रूरत संग्रह के माध्यम से लूप करने की होती है और आप किसी वास्तविक अंतर्निहित संग्रह के संदर्भ को सुरक्षित रूप से वापस कर सकते हैं, बिना इस डर के कि यह गलत तरीके से बदल जाएगा, आदि?

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


"सुरक्षित रूप से एक वास्तविक अंतर्निहित संग्रह के संदर्भ को इस डर के बिना लौटा दें कि यह गलत तरीके से बदल जाएगा" - भले ही आप IEnumerable <T> वापस करते हैं, क्या वे इसे केवल सूची <T> पर वापस नहीं डाल सकते हैं और इसे बदल सकते हैं?
कोबी

प्रत्येक IEnumarable <T> भी एक सूची नहीं है <T>। यदि दी गई वस्तु सूची के प्रकार से नहीं है, जो सूची <T> से प्राप्त होती है या IList <T> को लागू करती है, तो इसका परिणाम InvalidCastException में होगा।
तड़के

2
सूची <T> की समस्या है कि यह आपको एक विशेष कार्यान्वयन संग्रह में लॉक करता है <T> या ReadOnlyCollection <T> को प्राथमिकता दी जाती है
सैम केसर

4

मुझे लगता है कि आप या तो उपयोग कर सकते हैं, लेकिन प्रत्येक का उपयोग है। मूल रूप से ListहैIEnumerable लेकिन आपके पास गणना की कार्यक्षमता है, तत्व जोड़ें, तत्व निकालें

तत्वों की गणना के लिए IEnumerable कुशल नहीं है

यदि संग्रह को पढ़ने के लिए अभिप्रेत है, या संग्रह का संशोधन Parentतब के लिए नियंत्रित किया जाता है, तो IListबस वापस करने के लिएCount एक अच्छा विचार नहीं है।

लाइनक में, एक Count()विस्तार विधि है IEnumerable<T>जिस पर सीएलआर के अंदर .Countअंतर्निहित प्रकार के होने पर शॉर्टकट होगाIList , इसलिए प्रदर्शन अंतर नगण्य है।

आम तौर पर मुझे लगता है (राय) यह संभव है कि IEnumerable को वापस करने के लिए बेहतर अभ्यास जहां संभव हो, अगर आपको अतिरिक्त करने की आवश्यकता है, तो इन विधियों को मूल वर्ग में जोड़ें, अन्यथा उपभोक्ता तब मॉडल के भीतर संग्रह का प्रबंधन कर रहा है जो सिद्धांतों का उल्लंघन करता है, जैसे कि manufacturer.Models.Add(model)कानून का उल्लंघन Demeter। बेशक ये केवल दिशा-निर्देश हैं और कठिन और तेज़ नियम नहीं हैं, लेकिन जब तक आपके पास प्रयोज्यता का पूरा आभास नहीं होता है, तब तक आँख बंद करके पालन करना बेहतर है।

public interface IManufacturer 
{
     IEnumerable<Model> Models {get;}
     void AddModel(Model model);
}

(नोट: यदि nNHibernate का उपयोग करते हुए आपको अलग-अलग एक्सेसरों का उपयोग करके निजी IList के लिए मैप करना पड़ सकता है।)


2

यह इतना आसान नहीं है जब आप इनपुट मापदंडों के बजाय रिटर्न वैल्यू के बारे में बात कर रहे हैं। जब यह एक इनपुट पैरामीटर है, तो आप वास्तव में जानते हैं कि आपको क्या करने की आवश्यकता है। इसलिए, यदि आपको संग्रह पर पुनरावृत्ति करने में सक्षम होने की आवश्यकता है, तो आप एक IEnumberable लेते हैं जबकि यदि आपको जोड़ने या हटाने की आवश्यकता होती है, तो आप एक IList लेते हैं।

वापसी मूल्य के मामले में, यह कठिन है। आपके कॉलर को क्या उम्मीद है? यदि आप एक IEnumerable लौटाते हैं, तो वह एक प्राथमिकता नहीं जानता है कि वह इससे एक IList बना सकता है। लेकिन, यदि आप एक आईएलस्ट वापस करते हैं, तो उसे पता चल जाएगा कि वह उस पर पुनरावृति कर सकता है। तो, आपको यह ध्यान रखना होगा कि आपका कॉलर डेटा के साथ क्या करने जा रहा है। आपके फोन करने वाले को जिस कार्यक्षमता की जरूरत है / उम्मीद है वह यह है कि निर्णय क्या करना चाहिए, क्या करना चाहिए।


0

जैसा कि सभी ने कहा है कि यह निर्भर करता है, अगर आप कॉलिंग लेयर पर ऐड / फंक्शनलioanlity नहीं जोड़ना चाहते हैं तो मैं IEnumerable को वोट दूंगा क्योंकि यह केवल पुनरावृत्ति और बुनियादी कार्यक्षमता प्रदान करता है जो मुझे पसंद है। IList लौटते हुए मेरे वोट हमेशा फिर से होते हैं, लेकिन यह मुख्य रूप से आपको पसंद है और क्या नहीं। प्रदर्शन के संदर्भ में मुझे लगता है कि वे समान हैं।


0

यदि आप अपने बाहरी कोड में गिनती नहीं करते हैं, तो IEnumerable को वापस करना हमेशा बेहतर होता है, क्योंकि बाद में आप अपना कार्यान्वयन (बाहरी कोड प्रभाव के बिना) बदल सकते हैं, उदाहरण के लिए, उपज इट्रेटर के लिए लॉजिक और मेमोरी रिसोर्स को संरक्षित करना (बहुत अच्छी भाषा सुविधा) )।

हालाँकि, यदि आपको वस्तुओं की गिनती की आवश्यकता है, तो यह न भूलें कि IEnumerable और IList के बीच एक और परत है - ICollection के


0

मैं यहां से थोड़ा हटकर हो सकता हूं, यह देखकर कि किसी और ने अब तक इसका सुझाव नहीं दिया है, लेकिन आप क्यों नहीं लौटते हैं (I)Collection<T> ?

मुझे जो याद है, Collection<T>वह पसंदीदा रिटर्न प्रकार था List<T>क्योंकि यह अमूर्तता को दूर करता है। वे सभी लागू करते हैं IEnumerable, लेकिन यह मुझे नौकरी के लिए बहुत कम लगता है।


0

मुझे लगता है कि आप या तो उपयोग कर सकते हैं, लेकिन प्रत्येक का उपयोग है। मूल रूप से Listहै, IEnumerableलेकिन आपके पास गिनती कार्यक्षमता है, तत्व जोड़ें, तत्व निकालें

IEnumerable तत्वों की गिनती, या संग्रह में एक विशिष्ट तत्व प्राप्त करने के लिए कुशल नहीं है।

List एक ऐसा संग्रह है जो विशिष्ट तत्वों को खोजने, तत्वों को जोड़ने या उन्हें हटाने के लिए आदर्श रूप से अनुकूल है।

आम तौर पर मैं उपयोग करने की कोशिश करता हूं List जहां संभव हो क्योंकि इससे मुझे अधिक लचीलापन मिलता है।

के List<FooBar> getRecentItems() बजाय का उपयोग करें IList<FooBar> GetRecentItems()


0

मुझे लगता है कि सामान्य नियम वापसी के लिए अधिक विशिष्ट वर्ग का उपयोग करना है, अनावश्यक काम करने से बचने और अपने कॉलर को अधिक विकल्प देने के लिए।

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

याद रखें कि एक इंटरफ़ेस में IEnumerable से एक संग्रह के लिए UP बढ़ रहा है, एक संग्रह से IEnumerable तक नीचे जाने से मौजूदा कोड टूट जाएगा।

यदि ये राय सभी परस्पर विरोधी लगती हैं, तो यह इसलिए है क्योंकि निर्णय व्यक्तिपरक है।


0

टी एल; डॉ; - सारांश

  • यदि आप इन-हाउस सॉफ़्टवेयर विकसित करते हैं, तो विशिष्ट प्रकार का उपयोग करें (जैसे List रिटर्न मानों के लिए ) का और संग्रह के मामले में इनपुट मापदंडों के लिए सबसे सामान्य प्रकार भी।
  • यदि कोई विधि किसी पुनर्वितरण योग्य लाइब्रेरी के सार्वजनिक API का एक भाग है, तो रिटर्न मान और इनपुट पैरामीटर दोनों को शुरू करने के लिए ठोस संग्रह प्रकारों के बजाय इंटरफेस का उपयोग करें।
  • यदि कोई विधि केवल-पढ़ने के लिए संग्रह लौटाती है, तो उसका उपयोग करके IReadOnlyListया IReadOnlyCollectionवापसी मान प्रकार के रूप में दिखाएं ।

अधिक

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