2008-11-25 86 views
37

在我吮吸少的永恆追求中,我試圖理解「收益率」的說法,但我一直遇到同樣的錯誤。因爲 'System.Collections.Generic.List < ACLASS>不是一個迭代器接口類型一些幫助理解「收益率」

的〔的someMethod]身體不能是迭代器塊。

這是我被困代碼:

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

我在做什麼錯?我不能在迭代器中使用yield嗎?那有什麼意義? 在這個例子中它說List<ProductMixHeader>不是一個迭代器接口類型。 ProductMixHeader是一個自定義類,但我想List是一個迭代器接口類型,不是嗎?

- 編輯 -
感謝您的所有快速解答。
我知道這個問題不是全部新的,同樣的資源不斷彈出。
事實證明,我想我可以返回List<AClass>作爲返回類型,但由於List<T>不懶,它不能。改變我的返回類型IEnumerable<T>解決了這個問題:d

一個有點相關的問題(不值得打開一個新的線程):這是值得給予IEnumerable<T>作爲返回類型,如果我敢肯定的99%的情況我m會去.ToList()呢?性能影響會是什麼?

+6

我喜歡積極的方法'追求少吸';-)。 – 2008-11-25 14:21:04

+0

這個幾乎相同的問題有一個很好的雷蒙德陳的東西的鏈接:http://stackoverflow.com/questions/39476/what-is-the-yield-keyword-used-for-in-c – 2008-11-25 14:24:32

回答

32

使用收率返回的方法必須被聲明爲返回下面的兩個接口中的一個:

IEnumerable<SomethingAppropriate> 
IEnumerator<SomethingApropriate> 

(感謝JonMarc爲指出的IEnumerator)

實施例:

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

產量是一個懶惰的數據生產者,只生產另一個項目af第一個已被檢索,而返回列表將一次返回所有內容。

所以有區別,你需要正確地聲明方法。

欲瞭解更多信息,請閱讀Jon's answer here,其中包含一些非常有用的鏈接。

+2

爲了記錄:或IEnumerator [] – 2008-11-25 14:23:18

+2

它也可以聲明爲返回IEnumerator或IEnumerator 。 – 2008-11-25 14:23:25

0

你使用的方法是什麼樣的?我不認爲這可以單獨用於循環。

例如...

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

列表實現IEnumerable。

下面是一個示例,它可能會闡明您嘗試學習的內容。我寫了這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

「產量」創建一個迭代器塊 - 這可以實現無論是IEnumerable[<T>]IEnumerator[<T>]編譯器生成的類。 Jon Skeet在C# in Depth的第6章對此有非常好的(而且是免費的)討論。

但基本上 - 要使用「收益率」,您的方法必須返回IEnumerable[<T>]IEnumerator[<T>]。在這種情況下:

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

我強烈建議使用Reflector看看yield實際上爲你做了什麼。在使用yield時,您將能夠看到編譯器爲您生成的類的完整代碼,並且我發現人們能夠在看到低級結果時更快地理解這個概念(嗯,級別我猜)。

0

@I P的答案幫助我理解了產量以及爲什麼使用它。 yield的一個(主要)使用情況是在「in」關鍵字之後的「foreach」循環中不返回完整列表。不是一次返回完整列表,而是在每個「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,你需要了解什麼時候使用IEnumeratorIEnumerable(因爲你必須使用其中任何一個) 。以下示例可幫助您瞭解差異。

首先,看看下面的類,它實現了兩個方法 - 一個返回IEnumerator<int>,一個返回IEnumerable<int>。 ,

// 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; }  
    } 
} 

現在,如果你使用IterateOne你可以做到以下幾點::我會告訴你,有在使用中有很大的區別,雖然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。B)打印:

3
4
...
100

,因爲它繼續語句都被執行後1.A右)計數。

您可以看到,您可以使用MoveNext()逐項推進。


相比之下,IterateAll允許您使用foreachLINQ報表更大的安慰:

// 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>是仿製藥,他們可以與任何類型一起使用。但是,爲了簡單起見,在我的示例中使用了int,其類型爲T

這意味着,您可以使用返回類型IEnumerator<ProductMixHeader>IEnumerable<ProductMixHeader>(您在問題中提到的自定義類)之一。

List<ProductMixHeader>類型沒有實現任何這些接口,這就是爲什麼你不能這樣使用它的原因。但是例2.b)顯示瞭如何從中創建一個列表。