2012-10-17 26 views
2

我寫了這個:爲什麼不能使用迭代器塊與IOrderedEnumerable

using System;using System.Linq; 
static class MyExtensions 
{ 
    public static IEnumerable<T> Inspect<T> (this IEnumerable<T> source) 
    { 
     Console.WriteLine ("In Inspect"); 
     //return source; //Works, but does nothing 
     foreach(T item in source){ 
      Console.WriteLine(item); 
      yield return item; 
     } 
    } 
} 

然後去與這個來測試它:

var collection = Enumerable.Range(-5, 11) 
    .Select(x => new { Original = x, Square = x * x }) 
    .Inspect() 
    .OrderBy(x => x.Square) 
    //.Inspect() 
    .ThenBy(x => x.Original) 
    ; 
foreach (var element in collection) 
{ 
Console.WriteLine(element); 
} 

第一次使用的Inspect()工作正常。第二個評論說,不會編譯。 OrderBy的回報爲IOrderedEnumerable。我還以爲IOrderedEnumerable是,一個IEnumerable但是,與拳滾,我想:

public static IOrderedEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source) 
{ 
    Console.WriteLine ("In Inspect (ordered)"); 
    foreach(T item in source){ 
     Console.WriteLine(item); 
     yield return item; 
    } 
} 

但這不會編譯任。我被告知我不能有一個迭代器塊,因爲System.Linq.IOrderedEnumberable不是一個迭代器接口類型。

我錯過了什麼?我不明白爲什麼人們不希望按照與原始集合相同的方式迭代有序集合。

(使用單聲道2.10.8.1,這有效地C#是4.0,和MonoDevelop的2.8.6.3)

UPDATE:

作爲joshgo麻煩指出的,我可以採取的IOrderedEnumerable輸入參數,它的確行事as-aIEnumerable。但爲了迭代,我必須返回IEnumerable,並且我的原始錯誤是由ThenBy引起的,它堅持給出IOrderedEnumerable。非常合理。但有沒有辦法在這裏滿足ThenBy

UPDATE2:有沒有意義的,因爲:

在兩個答案的代碼打(這兩者都是非常有幫助),我終於明白了爲什麼我不能用產量與IOrderedEnumerable回來後這些值必須完全可用才能進行排序。因此,不用一個帶有yield的循環,我最好使用一個循環來打印所有項目,然後在最後一次返回源代碼。

回答

2

我認爲錯誤的解釋可以在這裏找到:Some help understanding "yield"

引用的Lasse五卡爾森:

使用收益回報的方法必須聲明爲返回 以下兩個接口之一:IEnumerable或IEnumerator

這個問題似乎與yield運算符和第二個函數的返回類型IOrderedEnumerable

如果將返回類型從IOrderedEnumerable更改爲IEnumerable,則第二次Inspect()調用將不再是錯誤。但是,ThenBy()調用現在將引發錯誤。如果您臨時將其註釋掉,它會編譯,但您無法訪問ThenBy()方法。

var collection = Enumerable.Range(-5, 11) 
    .Select(x => new { Original = x, Square = x * x }) 
    .Inspect() 
    .OrderBy(x => x.Square) 
    .Inspect() 
    //.ThenBy(x => x.Original) 
    ; 
foreach (var element in collection) 
{ 
    Console.WriteLine(element); 
} 

...

public static IEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source) 
{ 
    Console.WriteLine ("In Inspect (ordered)"); 
    foreach(T item in source){ 
     Console.WriteLine(item); 
     yield return item; 
    } 
} 
+1

這完全不回答這個問題:/ – Earlz

+0

@Earlz不會! (啊,我看你在joshgo做了一個重大的編輯之前寫了你的評論。) –

+0

啊哈!你是對的,問題在於對ThenBy()的調用。實際上'IEnumerable 檢查(這個IEnumerable 源)''OrderBy()'沒有問題。但重寫不能僅僅通過返回類型來改變;我想知道是否有辦法滿足ThenBy? –

2

如果你想申請手術後你的擴展方法,它返回IOrdereEnumerable,繼續訂購,那麼您需要創建第二個重載擴展:

public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source) 
{ 
    Console.WriteLine("In Ordered Inspect"); 
    // inspected items will be unordered 
    Func<T, int> selector = item => { 
       Console.WriteLine(item); 
       return 0; }; 

    return source.CreateOrderedEnumerable(selector, null, false);  
} 

什麼是這裏有意思:

  • 您需要申請退貨IOrderedEnumerable才能申請ThenByThenByDescending
  • IOrderedEnumerable不是通過yield return創建的。在你的情況下,它可以通過從源創建它
  • 您應該創建虛擬選擇器,它不會中斷項目的排序
  • 由於選擇器的執行順序與輸入順序相同,所以輸出將不包含有序項目。

如果您想查看訂購商品,您需要執行您的OrderedEnumerable。這將迫使所有運營商的執行,其中存在以前Inspect:似乎

public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source) 
{ 
    Console.WriteLine("In Ordered Inspect");    
    var enumerable = source.CreateOrderedEnumerable(x => 0, null, false);  
    // each time you apply Inspect all query until this operator will be executed 
    foreach(var item in enumerable) 
     Console.WriteLine(item); 
    return enumerable;  
} 
+0

感謝LazyB。很有意思。我認爲你的第二個例子可以簡化爲:'Console.WriteLine(「In Ordered Inspect」); foreach(源項目中的T項){Console.WriteLine(「Inspect(O):」+ item);} return source;'(?) –

+0

@DarrenCook是的,你說得對。如果我們不排序,我們可以返回源代碼 –

相關問題