171

このコードを考えると:

IEnumerable<object> FilteredList()
{
    foreach( object item in FullList )
    {
        if( IsItemInPartialList( item ) )
            yield return item;
    }
}

なぜこのようにコーディングしないほうがよいのでしょうか?:

IEnumerable<object> FilteredList()
{
    var list = new List<object>(); 
    foreach( object item in FullList )
    {
        if( IsItemInPartialList( item ) )
            list.Add(item);
    }
    return list;
}

yieldキーワードの意味がなんとなくわかりました。これは、特定の種類のもの (反復子) を構築するようにコンパイラに指示します。しかし、なぜそれを使用するのですか?コードがわずかに少ないことを除けば、それは私にとって何をしますか?

4

8 に答える 8

241

を使用yieldすると、コレクションが遅延します。

最初の 5 つの項目だけが必要だとします。おっしゃるとおり、リスト全体をループして最初の 5 つの項目を取得する必要があります。ではyield、最初の 5 つの項目だけをループします。

于 2012-12-27T16:28:09.033 に答える
128

イテレータ ブロックの利点は、遅延して動作することです。したがって、次のようなフィルタリング メソッドを記述できます。

public static IEnumerable<T> Where<T>(this IEnumerable<T> source,
                                   Func<T, bool> predicate)
{
    foreach (var item in source)
    {
        if (predicate(item))
        {
            yield return item;
        }
    }
}

これにより、一度に複数のアイテムをバッファリングすることなく、好きなだけストリームをフィルタリングできます。たとえば、返されたシーケンスの最初の値のみが必要な場合、なぜすべてを新しいリストにコピーする必要があるのでしょうか?

別の例として、反復子ブロックを使用して無限ストリームを簡単に作成できます。たとえば、乱数のシーケンスは次のとおりです。

public static IEnumerable<int> RandomSequence(int minInclusive, int maxExclusive)
{
    Random rng = new Random();
    while (true)
    {
        yield return rng.Next(minInclusive, maxExclusive);
    }
}

無限シーケンスをリストにどのように格納しますか?

私のEdulinq ブログ シリーズでは、反復子ブロックを多用するLINQ to Objects のサンプル実装を紹介しています。LINQ は根本的に怠け者であり、物事をリストに入れることは単純にそのようには機能しません。

于 2012-12-27T16:29:59.427 に答える
42

「リスト」コードでは、次のステップに渡す前に完全なリストを処理する必要があります。「利回り」バージョンは、処理されたアイテムをすぐに次のステップに渡します。その「次のステップ」に「.Take(10)」が含まれている場合、「yield」バージョンは最初の 10 項目のみを処理し、残りは無視します。「リスト」コードはすべてを処理します。

これは、多くの処理を行う必要がある場合や、処理するアイテムのリストが長い場合に、最も大きな違いが見られることを意味します。

于 2012-12-27T16:29:19.783 に答える
23

yieldリストにない項目を返すために使用できます。これは、キャンセルされるまでリストを無限に繰り返すことができる小さなサンプルです。

public IEnumerable<int> GetNextNumber()
{
    while (true)
    {
        for (int i = 0; i < 10; i++)
        {
            yield return i;
        }
    }
}

public bool Canceled { get; set; }

public void StartCounting()
{
    foreach (var number in GetNextNumber())
    {
        if (this.Canceled) break;
        Console.WriteLine(number);
    }
}

これは書いています

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4

...等。キャンセルされるまでコンソールに。

于 2012-12-27T16:32:18.280 に答える
10
object jamesItem = null;
foreach(var item in FilteredList())
{
   if (item.Name == "James")
   {
       jamesItem = item;
       break;
   }
}
return jamesItem;

上記のコードを使用して FilteredList() をループし、 item.Name == "James" がリストの 2 番目の項目で満たされると仮定すると、使用yieldするメソッドは 2 回生成されます。これは怠惰な動作です。

リストを使用するメソッドは、n個のオブジェクトすべてをリストに追加し、完全なリストを呼び出し元のメソッドに渡します。

これはまさに、IEnumerable と IList の違いを際立たせるユース ケースです。

于 2012-12-27T16:30:39.960 に答える
8

の使用について私が見た中で最も良い実世界の例はyield、フィボナッチ数列を計算することです。

次のコードを検討してください。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(string.Join(", ", Fibonacci().Take(10)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(15).Take(1)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(10).Take(5)));
        Console.WriteLine(string.Join(", ", Fibonacci().Skip(100).Take(1)));
        Console.ReadKey();
    }

    private static IEnumerable<long> Fibonacci()
    {
        long a = 0;
        long b = 1;

        while (true)
        {
            long temp = a;
            a = b;

            yield return a;

            b = temp + b;
        }
    }
}

これは以下を返します:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55
987
89, 144, 233, 377, 610
1298777728820984005

これは、無限級数をすばやく簡単に計算できるので便利です。Linq 拡張機能を使用して、必要なものだけを照会できます。

于 2013-04-22T22:42:17.940 に答える
1

[収量] を使用する理由 コードがわずかに少ないことを除けば、それは私にとって何をしますか?

役立つ場合もあれば、そうでない場合もあります。データのセット全体を調べて返す必要がある場合、yield を使用してもオーバーヘッドが発生するだけなので、yield を使用しても何のメリットもありません。

yield が本当に輝くのは、部分的なセットのみが返されるときです。最良の例はソートだと思います。今年の日付と金額を含むオブジェクトのリストがあり、その年の最初の一握り (5) レコードを表示したいとします。

これを行うには、リストを日付の昇順で並べ替え、最初の 5 つを取得する必要があります。これが yield なしで行われた場合、最後の 2 つの日付が正しいことを確認するまで、リスト全体をソートする必要があります。

ただし、yield を使用すると、最初の 5 つの項目が確立されると、並べ替えが停止し、結果が利用可能になります。これにより、時間を大幅に節約できます。

于 2014-07-23T23:31:38.793 に答える
0

yield return ステートメントでは、一度に 1 つのアイテムのみを返すことができます。リスト内のすべてのアイテムを収集し、そのリストを再度返すため、メモリ オーバーヘッドが発生します。

于 2013-01-02T11:48:21.327 に答える