2008-11-25 14 views
37

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

[someMethod] के शरीर पुनरावर्तक ब्लॉक नहीं किया जा सकता क्योंकि 'System.Collections.Generic.List < Aclass>' पुनरावर्तक इंटरफ़ेस प्रकार नहीं है।

foreach (XElement header in headersXml.Root.Elements()){ 
    yield return (ParseHeader(header));     
} 

क्या मैं गलत कर रहा हूँ:

इस कोड को मैं कहाँ अटक गया है? क्या मैं इटेटरेटर में उपज का उपयोग नहीं कर सकता? तो बात क्या है? इस उदाहरण में यह कहा गया है कि List<ProductMixHeader> एक इटरेटर इंटरफ़ेस प्रकार नहीं है। ProductMixHeader एक कस्टम क्लास है, लेकिन मुझे लगता है कि List एक इटरेटर इंटरफ़ेस प्रकार है, नहीं?

- एडिट -
सभी त्वरित उत्तरों के लिए धन्यवाद।
मुझे पता है कि यह सवाल इतना नया नहीं है और वही संसाधन पॉप-अप रहते हैं।
यह पता चला कि मैं सोच रहा था कि मैं रिटर्न प्रकार के रूप में List<AClass> वापस कर सकता हूं, लेकिन List<T> आलसी नहीं है, यह नहीं हो सकता है। समस्या हल करने के लिए अपने IEnumerable<T> वापसी प्रकार बदलने: डी

एक कुछ हद तक संबंधित सवाल (एक नई धागा खोलने के लायक नहीं): यह IEnumerable<T> एक वापसी प्रकार के रूप में देने के लायक है, तो मुझे यकीन है कि मामलों के 99% मैं कर रहा हूँ है मैं जा रहा हूँ। टोस्टिस्ट() वैसे भी? प्रदर्शन प्रभाव क्या होगा?

+6

मुझे संभावित दृष्टिकोण 'कम चूसने के लिए खोज' पसंद है ;-)। –

+0

यह लगभग समान प्रश्न कुछ अच्छे रेमंड चेन सामानों का एक लिंक है: http://stackoverflow.com/questions/39476/what-is-the-yield-keyword-used-for-in-c –

उत्तर

32

उपज वापसी का उपयोग कर एक विधि निम्नलिखित दो इंटरफेस को वापस लौटाते समय के रूप में घोषित किया जाना चाहिए:

IEnumerable<SomethingAppropriate> 
IEnumerator<SomethingApropriate> 

(धन्यवाद Jon और Marc IEnumerator ओर इशारा करते हुए के लिए)

उदाहरण:

public IEnumerable<AClass> YourMethod() 
{ 
    foreach (XElement header in headersXml.Root.Elements()) 
    { 
     yield return (ParseHeader(header));     
    } 
} 

उपज डेटा का आलसी उत्पादक है, केवल एक और आइटम का उत्पादन पहले को पुनः प्राप्त कर लिया गया है, जबकि एक सूची लौटने से सब कुछ एक ही बार में वापस आ जाएगा।

तो एक अंतर है, और आपको विधि को सही तरीके से घोषित करने की आवश्यकता है।

अधिक जानकारी के लिए, Jon's answer here पढ़ें, जिसमें कुछ बहुत ही उपयोगी लिंक शामिल हैं।

+2

रिकॉर्ड के लिए: या आईन्यूमेरेटर [] –

+2

इसे आईन्यूमेरेटर या आईन्यूमेरेटर वापस करने के लिए भी घोषित किया जा सकता है। –

+4

इसे 7 सेकंड से पीटा गया;) –

0

आप जिस तरीके से इसका उपयोग कर रहे हैं, वह कैसा दिखता है? मुझे नहीं लगता कि इसका इस्तेमाल सिर्फ एक लूप में ही किया जा सकता है।

उदाहरण के लिए ...

public IEnumerable<string> GetValues() { 
    foreach(string value in someArray) { 
     if (value.StartsWith("A")) { yield return value; } 
    } 
} 
3

सूची IEnumerable लागू करता है।

यहां एक उदाहरण है जो आप सीखने की कोशिश कर रहे कुछ प्रकाश डाल सकते हैं।एक संकलक उत्पन्न वर्ग है कि या तो IEnumerable[<T>] या IEnumerator[<T>] लागू कर सकते हैं - मैं इस बारे में 6 महीने

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace YieldReturnTest 
{ 
    public class PrimeFinder 
    { 
     private Boolean isPrime(int integer) 
     { 
      if (0 == integer) 
       return false; 

      if (3 > integer) 
       return true; 

      for (int i = 2; i < integer; i++) 
      { 
       if (0 == integer % i) 
        return false; 
      } 
      return true; 
     } 

     public IEnumerable<int> FindPrimes() 
     { 
      int i; 

      for (i = 1; i < 2147483647; i++) 
      { 
       if (isPrime(i)) 
       { 
        yield return i; 
       } 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      PrimeFinder primes = new PrimeFinder(); 

      foreach (int i in primes.FindPrimes()) 
      { 
       Console.WriteLine(i); 
       Console.ReadLine(); 
      } 

      Console.ReadLine(); 
      Console.ReadLine(); 
     } 
    } 
} 
8

लिखा था "उपज" पुनरावर्तक ब्लॉक बनाता है। C# in Depth के अध्याय 6 में जॉन स्कीट की एक बहुत अच्छी (और नि: शुल्क) चर्चा है।

लेकिन मूल रूप से - "उपज" का उपयोग करने के लिए आपकी विधि को IEnumerable[<T>] या IEnumerator[<T>] वापस करना होगा। इस मामले में:

public IEnumerable<AClass> SomeMethod() { 
    // ... 
    foreach (XElement header in headersXml.Root.Elements()){ 
     yield return (ParseHeader(header));     
    } 
} 
+0

धन्यवाद! ऐसा माना जाता है कि सूची ने आईनेमरेबल लागू किया है, लेकिन यह गारंटी नहीं देता है .. अब क्या आपको अतिरिक्त उपज मील जाने का मौका मिलता है यदि आपको कुछ विधि()। ToList() को सड़क के नीचे कुछ लाइनों पर जाना है? अगर मेरी समझ सही मायने में मेरी सेवा करती है, तो यह उपज के पूरे उद्देश्य को हरा देती है, है ना? –

+0

@boris - सूची IENumerable लागू करता है - लेकिन यह इंगित नहीं करता है।एक इटरेटर ब्लॉक बनाने के लिए आपको * ienumerable/ienumerator इंटरफ़ेस स्वयं ही वापस करना होगा। यह किसी और चीज के लिए परिभाषित नहीं है। –

+0

@boris - "पूरे उद्देश्य को हराता है" - बिल्कुल नहीं ;-p बहुत सारे उपयोग हैं जहां एक स्ट्रीमिंग एपीआई (जैसे आईनेमेरेबल ) एक बफर किए गए संग्रह (जैसे सूची ) के लिए बेहतर है - खासकर अगर आप कई हजार रिकॉर्ड (फाइल या डेटाबेस से) से निपट रहे हैं। –

14

यह एक मुश्किल विषय है। संक्षेप में, यह IENumerable और उसके दोस्तों को लागू करने का एक आसान तरीका है। कंपाइलर आपको एक राज्य मशीन बनाता है, पैरामीटर और स्थानीय चर को नए वर्ग में आवृत्ति चर में बदल देता है। जटिल सामान

मैं इस पर कुछ संसाधन हैं:

+1

ग्रेट सामान! मुझे आपकी पुस्तक में 6.3 सूची शुरू करके इस विषय को समझाया गया था - जिसने पूरी तरह से स्पष्ट किया कि इटरेटर और उपज कथन के पीछे क्या विचार है। इसे साझा करने के लिए आपको धन्यवाद! – Matt

+0

हाय जॉन, अध्याय 6 का लिंक वेब के किनारे से गिर गया है, मुझे अब [मैनिंग प्रकाशन] (https://www.manning.com/) पर वापस जाना है, क्या आपके पास एक वैकल्पिक लिंक है? –

+1

@ LasseV.Karlsen: मैंने गहराई पृष्ठ में सी # पर जाने के लिए लिंक अपडेट किया है, जहां आप अध्याय स्वयं डाउनलोड कर सकते हैं। –

3

मैं अत्यधिक उपयोग करने की अनुशंसा Reflector पर एक नज़र डालने के लिए yield वास्तव में आपके लिए क्या करता है। उपज का उपयोग करते समय आप उस वर्ग का पूरा कोड देख सकेंगे जो संकलक आपके लिए उत्पन्न करता है, और मैंने पाया है कि लोग अवधारणा को और अधिक तेज़ी से समझते हैं जब वे निम्न-स्तर के परिणाम देख सकते हैं (ठीक है, मध्य- स्तर मुझे लगता है)।

0

@ इयान पी के उत्तर ने मुझे उपज को समझने में बहुत मदद की और इसका उपयोग क्यों किया जाता है। उपज के लिए एक (प्रमुख) उपयोग केस "इन" कीवर्ड के बाद "foreach" loops में पूरी तरह से पूर्ण सूची वापस नहीं लौटाता है। एक बार में पूरी सूची लौटने के बजाय, प्रत्येक "foreach" लूप में केवल एक आइटम (अगली वस्तु) लौटा दी जाती है। तो आप ऐसे मामलों में उपज के साथ प्रदर्शन हासिल करेंगे। मैं निम्नलिखित करने के लिए अपने बेहतर समझ के लिए @Ian P's कोड फिर से लिखा है:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace YieldReturnTest 
{ 
    public class PrimeFinder 
    { 
     private Boolean isPrime(int integer) 
     { 
      if (0 == integer) 
       return false; 

      if (3 > integer) 
       return true; 

      for (int i = 2; i < integer; i++) 
      { 
       if (0 == integer % i) 
        return false; 
      } 
      return true; 
     } 

     public IEnumerable<int> FindPrimesWithYield() 
     { 
      int i; 

      for (i = 1; i < 2147483647; i++) 
      { 
       if (isPrime(i)) 
       { 
        yield return i; 
       } 
      } 
     } 

     public IEnumerable<int> FindPrimesWithoutYield() 
     { 
      var primes = new List<int>(); 
      int i; 
      for (i = 1; i < 2147483647; i++) 
      { 
       if (isPrime(i)) 
       { 
        primes.Add(i); 
       } 
      } 
      return primes; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      PrimeFinder primes = new PrimeFinder(); 

      Console.WriteLine("Finding primes until 7 with yield...very fast..."); 
      foreach (int i in primes.FindPrimesWithYield()) // FindPrimesWithYield DOES NOT iterate over all integers at once, it returns item by item 
      { 
       if (i > 7) 
       { 
        break; 
       } 
       Console.WriteLine(i); 
       //Console.ReadLine(); 

      } 

      Console.WriteLine("Finding primes until 7 without yield...be patient it will take lonkg time..."); 
      foreach (int i in primes.FindPrimesWithoutYield()) // FindPrimesWithoutYield DOES iterate over all integers at once, it returns the complete list of primes at once 
      { 
       if (i > 7) 
       { 
        break; 
       } 
       Console.WriteLine(i); 
       //Console.ReadLine(); 
      } 

      Console.ReadLine(); 
      Console.ReadLine(); 
     } 
    } 
} 
1

yield समझने के लिए, आपको यह समझना होगा जब IEnumerator और IEnumerable उपयोग करने के लिए (क्योंकि आप उनमें से किसी का भी उपयोग करने के लिए है) की जरूरत है । निम्नलिखित उदाहरण आपको अंतर को समझने में मदद करते हैं।

सबसे पहले, निम्न वर्ग पर एक नज़र डालें, यह दो विधियों को लागू करता है - एक IEnumerator<int> लौटा रहा है, एक IEnumerable<int> लौटा रहा है। अब, अगर आप IterateOne का उपयोग कर रहे

// 2 iterators, one as IEnumerator, one as IEnumerable 
public class Iterator 
{ 
    public static IEnumerator<int> IterateOne(Func<int, bool> condition) 
    { 
     for(var i=1; condition(i); i++) { yield return i; }  
    } 
    public static IEnumerable<int> IterateAll(Func<int, bool> condition) 
    { 
     for(var i=1; condition(i); i++) { yield return i; }  
    } 
} 

आप निम्न कर सकते हैं:: मैं आपको लगता है कि वहाँ के उपयोग में एक बड़ा अंतर है, दिखाता हूँ हालांकि 2 तरीकों में से कोड समान लग रही है

// 1. Using IEnumerator allows to get item by item 
    var i=Iterator.IterateOne(x => true); // iterate endless 
    // 1.a) get item by item 
    i.MoveNext(); Console.WriteLine(i.Current); 
    i.MoveNext(); Console.WriteLine(i.Current); 
    // 1.b) loop until 100 
    int j; while (i.MoveNext() && (j=i.Current)<=100) { Console.WriteLine(j); } 

1.a) प्रिंट:

1
2

1।ख) प्रिंट:

3
4
...
100

क्योंकि यह गिनती सही 1.a के बाद) के बयानों के बाद मार दिया जारी है।

आप देख सकते हैं कि आप MoveNext() का उपयोग करके आइटम द्वारा आइटम को अग्रिम कर सकते हैं।


इसके विपरीत, IterateAll और आप foreach उपयोग करने के लिए अनुमति देता है बड़ा आराम के लिए भी LINQ बयान:

// 2. Using IEnumerable makes looping and LINQ easier 
    var k=Iterator.IterateAll(x => x<100); // limit iterator to 100 
    // 2.a) Use a foreach loop 
    foreach(var x in k){ Console.WriteLine(x); } // loop 
    // 2.b) LINQ: take 101..200 of endless iteration 
    var lst=Iterator.IterateAll(x=>true).Skip(100).Take(100).ToList(); // LINQ: take items 
    foreach(var x in lst){ Console.WriteLine(x); } // output list 

2.a) प्रिंट:

1
2
। ..
99

2.b) प्रिंट:

101
102
...
200


नोट:IEnumerator<T> के बाद से और IEnumerable<T> जेनेरिक्स हैं, वे हो सकता है किसी भी प्रकार के साथ प्रयोग किया जाता है। हालांकि, सादगी के लिए मैंने T टाइप करने के लिए अपने उदाहरणों में int का उपयोग किया है।

इसका मतलब है, आप रिटर्न प्रकार IEnumerator<ProductMixHeader> या IEnumerable<ProductMixHeader> (आपके प्रश्न में वर्णित कस्टम क्लास) का उपयोग कर सकते हैं।

प्रकार List<ProductMixHeader> इनमें से किसी भी इंटरफेस को लागू नहीं करता है, यही कारण है कि आप इसे इस तरह उपयोग नहीं कर सकते हैं। लेकिन उदाहरण 2.b) दिखा रहा है कि आप इससे एक सूची कैसे बना सकते हैं।

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