2

私はこれを書いた:

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()動作します。コメントアウトされた2つ目は、コンパイルされません。の戻りは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はイテレーターインターフェイスタイプではないため、イテレーターブロックを使用できないと言われます。

私は何が欠けていますか?生のコレクションと同じように、順序付けられたコレクションを繰り返し処理したくない理由がわかりません。

(事実上C#4.0であるMono 2.10.8.1、およびMonoDevelop 2.8.6.3を使用)

アップデート:

joshgoが親切に指摘したように、私はの入力パラメーターを取ることができますIOrderedEnumerable、それは確かに-aとして機能し IEnumerableます。しかし、繰り返すには、を返す必要があります。元のエラーは、が与えられていることを主張するIEnumerableによって引き起こされました。非常に合理的でもあります。しかし、ここで満足する方法はありますか?ThenByIOrderedEnumerableThenBy

UPDATE2:

両方の回答(どちらも非常に役に立ちました)のコードで遊んだ後、IOrderedEnumerableリターンでyieldを使用できない理由を最終的に理解しました:値を実行するには値が完全に利用可能である必要があるため、意味がありません選別。したがって、yieldを含むループの代わりに、ループを使用してすべてのアイテムを印刷し、最後に1回だけソースを返すこともできます。

4

2 に答える 2

2

エラーの説明はここにあると思います:「歩留まり」を理解するのに役立つものもあります

ラッセV.カールセンの引用:

イールドリターンを使用するメソッドは、IEnumerableまたはIEnumeratorの2つのインターフェイスのいずれかを返すように宣言する必要があります。

問題は、yield演算子と2番目の関数の戻り値にあるよう IOrderedEnumerableです。

リターンタイプをからIOrderedEnumerableに変更するIEnumerableと、2番目の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;
    }
}
于 2012-10-17T03:19:23.407 に答える
2

操作後に拡張メソッドを適用して注文を返しIOrdereEnumerable、続行する場合は、2番目のオーバーロードされた拡張を作成する必要があります。

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申請するために戻る必要がありますThenByまたはThenByDescending
  • 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;    
}
于 2012-10-17T09:12:45.733 に答える