2010-09-23 4 views
40

मुझे पता है कि शायद यह अधिकांश भाग के लिए प्रदर्शन/प्रभाव को प्रभावित नहीं करता है, लेकिन मुझे IEnumerable प्राप्त करने और .Count() करने के विचार से नफरत है। क्या IsEmpty या NotEmpty या कुछ फ़ंक्शन है? (एसटीएल के लिए इसी तरह खाली())IENumerable खाली है?

+0

खालीपन की जांच करते समय गणना बहुत अक्षम हो सकती है जब अंतर्निहित संग्रह आईसीओलेक्शन नहीं है क्योंकि यह वापस लौटने से पहले सभी तत्वों की गणना करेगा जब आप जानना चाहते हैं कि कम से कम 1 तत्व है। –

+1

संभावित डुप्लिकेट [सी #: अनुक्रमित तरीका यह जांचने के लिए अनुक्रमित है कि कोई अनुक्रम खाली है या नहीं) (http://stackoverflow.com/questions/2094729/c-recommended-way-to-check-if-a-sequence-is-empty) – nawfal

उत्तर

71

आप IEnumerable.Any() एक्सटेंशन विधि (नेट फ्रेमवर्क 3.5 और ऊपर) चाहते हैं। यह तत्वों पर गिनती से बचाता है।

+6

विस्तृत करने के लिए इसे 1 तत्व पर गिना जाता है और यह बहुत ही कुशल है। –

+3

"... और बहुत कुशल है" - यह दक्षता कार्यान्वयन पर निर्भर करता है। यदि यह आलसी-मूल्यांकन गणना है (उदाहरण के लिए उपज का उपयोग करने वाली एक विधि), तो गणना शुरू करने से भी महंगा हो सकता है। – Joe

+9

महंगा इसका मतलब यह नहीं है कि इसका कुशल नहीं है। –

8

LINQ में से किसी की जरूरत के बिना, आप निम्न कर सकते हैं:

bool IsEmpty(IEnumerable en) 
{ 
    foreach(var c in en) { return false; } 
    return true; 
} 
+2

यहां तक ​​कि तेजी से वापस आ जाएगा। GetEnumerator()। MoveNext(), जो मूल रूप से कोई भी() का कार्यान्वयन करता है। आप फोरैच के साथ बहुत कुछ कर रहे हैं, लेकिन आप स्थानीय वैर को पहला मान निर्दिष्ट करने की अतिरिक्त लागतें ले रहे हैं, जिसका आप कभी भी उपयोग नहीं करेंगे। – KeithS

+0

@ किथ्स: यह बहुत सुरक्षित नहीं है: एक आईनेमरेबल भी आईडीस्पोज़ेबल हो सकता है (कई संख्याओं के लिए यह बहुत प्रासंगिक है, उदाहरण के लिए जब आपके प्रयास में अंततः आपके उपज बयान के आसपास होता है)। foreach सावधानी बरतता है आप ठीक से निपटान। बीटीडब्लू, उपरोक्त विधि काफी है जो 'कोई() 'करता है। – Ruben

+0

ठीक है, तो (iter = en.GetIterator()) का उपयोग करके इसे वापस करें। MoveNext(); आप अभी भी वर्तमान मूल्य को असाइन नहीं कर रहे हैं जैसे कि आप एक foreach के साथ होगा। – KeithS

1

IEnumerable या IEnumerable<T> पर, नहीं।

लेकिन यह वास्तव में ज्यादा समझ में नहीं आता है। यदि कोई संग्रह खाली है और आप IEnumerable का उपयोग करके इसे फिर से चालू करने का प्रयास करते हैं, तो IEnumerator.MoveNext() पर कॉल केवल प्रदर्शन लागत पर झूठी वापसी करेगा।

+0

"... कोई प्रदर्शन लागत नहीं है" - यदि यह संग्रह नहीं है, लेकिन इसके बजाय आलसी-मूल्यांकन गणना, तो एक महत्वपूर्ण लागत हो सकती है। – Joe

+0

@ जो - लेकिन आप उसी लागत को लेकर आएंगे जो IENumerable.Any() या IEnumerable.Count() को भी कॉल कर रहा है। –

+0

मैं सहमत हूं। इसलिए कुछ मामलों में एक सूची को तत्काल करने पर विचार करना उचित है ताकि यह लागत केवल एक बार हो। मेरा जवाब देखें – Joe

0

मुझे ऐसा नहीं लगता है, यही Count है। इसके अलावा, क्या तेजी से हो जाएगा:

  1. एक संपत्ति तक पहुंचना और उसे एक संग्रहीत Integer
  2. पुन: प्राप्त करने के लिए एक संपत्ति तक पहुंचना और उसे पुन: प्राप्त करने के लिए एक संग्रहीत Boolean
+2

आईन्यूमेरेबल पर गणना काफी महंगी हो सकती है (उदाहरण के लिए, आलसी मूल्यांकन वाले गणक)। मेरा समाधान है, मुझे लगता है, बेहतर है। – nothrow

+1

एक सूची या इसी तरह की वस्तु में जहां गणना आसानी से उपलब्ध है, यह सस्ता होगा। लेकिन एक सामान्य आईनेमरेबल कार्यान्वयन में, गणना() के लिए निष्पक्ष कार्यान्वयन तत्वों पर पुनरावृत्ति करना होगा, जो ओपी के लिए पूछे जाने वाले संभावित रूप से बहुत अधिक ओवरहेड है। – cristobalito

+0

@Yossarian: मुझे नहीं पता था कि एक 'गणना() '-फंक्शन है, मुझे लगता है कि हर संग्रह एक संपत्ति का उपयोग करता है जो एक निजी चर देता है। – Bobby

2

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

लेकिन आलसी मूल्यांकन (उदाहरण के लिए उपज का उपयोग करने वाली विधि) के मामले में, या तो महंगा हो सकता है। उदाहरण के लिए, निम्नलिखित IEnumerable कार्यान्वयन के साथ, प्रत्येक किसी भी करने के लिए कॉल या डेटाबेस के लिए एक नया गोल यात्रा की लागत का कारण बन जाएगा गणना:

IEnumerable<MyObject> GetMyObjects(...) 
{ 
    using(IDbConnection connection = ...) 
    { 
     using(IDataReader reader = ...) 
     { 
      while(reader.Read()) 
      { 
       yield return GetMyObjectFromReader(reader); 
      } 
     } 
    } 
} 

मुझे लगता है कि नैतिक है:

  • आप अगर केवल IEnumerable<T> है, और आप इसे केवल गणना करने के लिए अधिक करना चाहते हैं (उदाहरण के लिए गणना या कोई भी उपयोग करें), फिर पहले इसे एक सूची (एक्सटेंशन विधि ToList) में परिवर्तित करने पर विचार करें। इस तरह आप केवल एक बार गणना करने की गारंटी देते हैं।

  • आप एक API कि एक संग्रह रिटर्न को डिजाइन कर रहे हैं, बजाय IEnumerable<T> के रूप में कई लोगों को सिफारिश करने के लिए लग रहे हैं ICollection<T> (या यहां तक ​​IList<T>) लौटने पर विचार करें। ऐसा करके आप आलसी मूल्यांकन की गारंटी के लिए अपने अनुबंध को मजबूत कर रहे हैं (और इसलिए कोई एकाधिक मूल्यांकन नहीं)।

कृपया ध्यान दें मैं कह रहा हूँ आप एक संग्रह लौटने पर विचार करना चाहिए, नहीं हमेशा एक संग्रह लौट आते हैं। जैसा कि हमेशा व्यापार-बंद होते हैं, जैसा कि नीचे दी गई टिप्पणियों से देखा जा सकता है।

  • @KeithS सोचता है कि आप किसी DataReader पर उपज कभी नहीं करना चाहिए, और जब मैं कभी नहीं कभी नहीं कहते हैं, मैं इसे आम तौर पर ध्वनि सलाह है कि एक डेटा एक्सेस परत के बजाय एक ICollection<T> लौट जाना है कहेंगे एक आलसी से मूल्यांकन IEnumerable<T>, केथ्स ने अपनी टिप्पणी में कारणों के लिए।

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

+0

+ 1 अच्छा बिंदु। –

+3

यह एक बुरा उदाहरण है। प्रदर्शन के अलावा अन्य कारणों के लिए आपको डेटारिएडर पर कभी भी उपज नहीं करना चाहिए (यह ताले को लंबे समय तक रखता है, एक "फायरहोज" डेटा स्ट्रीम को ट्रिकल में बदल देता है)। हालांकि, सभी डेटा को एक सूची में slurping, फिर इसके माध्यम से उपज, समान प्रदर्शन करेंगे और अभी भी बिंदु को चित्रित करेंगे। – KeithS

+1

लौटने से पहले ToListing बार-बार तत्काल लागत की तुलना में अधिक महंगा हो सकता है, उदाहरण के लिए, डेटाबेस लाखों रिकॉर्ड लौटाता है। आईडी बल्कि IENumerable लौटाएं और उपभोक्ता को यह तय करने दें कि क्या वे ToList के साथ परिणामों को कैश करना चाहते हैं। इसके अलावा किथ ने क्या कहा। –

2

ध्यान रखें कि IENumerable केवल एक इंटरफ़ेस है। इसके पीछे कार्यान्वयन कक्षा से कक्षा तक बहुत अलग हो सकता है (जो के उदाहरण पर विचार करें)। एक्सटेंशन विधि IEnumerable.Any() को एक सामान्य दृष्टिकोण होना चाहिए और जो भी आप चाहते हैं (प्रदर्शन के अनुसार) नहीं हो सकता है। योसियन एक ऐसे साधन सुझाता है जो कई वर्गों के लिए काम करना चाहिए, लेकिन यदि अंतर्निहित कार्यान्वयन 'उपज' का उपयोग नहीं करता है तो भी आप कीमत का भुगतान कर सकते हैं।

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

0

तुम भी तो की तरह अपने खुद के गणना विस्तार विधि भार के लिख सकते हैं:

/// <summary> 
    /// Count is at least the minimum specified. 
    /// </summary> 
    /// <typeparam name="TSource"></typeparam> 
    /// <param name="source"></param> 
    /// <param name="min"></param> 
    /// <returns></returns> 
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min) 
    { 
     if (source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 
     return source.Count(min, int.MaxValue); 
    } 

    /// <summary> 
    /// Count is between the given min and max values 
    /// </summary> 
    /// <typeparam name="TSource"></typeparam> 
    /// <param name="source"></param> 
    /// <param name="min"></param> 
    /// <param name="max"></param> 
    /// <returns></returns> 
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min, int max) 
    { 
     if (source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 
     if (min <= 0) 
     { 
      throw new ArgumentOutOfRangeException("min", "min must be a non-zero positive number"); 
     } 
     if (max <= 0) 
     { 
      throw new ArgumentOutOfRangeException("max", "max must be a non-zero positive number"); 
     } 
     if (min >= max) 
      throw new ArgumentOutOfRangeException("min and max", "min must be lest than max"); 

     var isCollection = source as ICollection<TSource>; 

     if (isCollection != null) 
      return isCollection.Count >= min && isCollection.Count <= max; 

     var count = 0; 
     using (var enumerator = source.GetEnumerator()) 
     { 
      while (enumerator.MoveNext()) 
      { 
       count++; 
       if (count >= min && count <= max) 
        return true; 
      } 
     } 
     return false; 
    } 
13

अगर यह इस

enumeration.Cast<object>().Any(); 

तरह ख़ाली से साधारण नहीं है अगर यह सामान्य है, Enumerable के उपयोग के विस्तार के रूप में पहले से ही कहा गया है

0

Enumerable.Empty() का उपयोग करें जो IENumerable.Any() की बजाय शून्य सूची को समाप्त करने से रोक देगा।

+0

मुझे लगता है कि उपयोगकर्ता यह जांचने का आसान तरीका चाहता है कि कोई आईनेमरेबल खाली है या नहीं। संख्यात्मक। लक्षण() एक आईनेमरेबल देता है। लेकिन IENumerable.Any() बूलियन मान देता है। क्योंकि मैं इस सवाल के लिए प्रासंगिक सोचता हूं Inumerable.Any() बेहतर है। https://msdn.microsoft.com/en-us/library/bb341042(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/bb337697(v=vs। 110) .aspx –