0

yield return私は自分が書いているコードのいくつかを使ってステートメントをテストしてきました。私には2つの方法があります:

public static IEnumerable<String> MyYieldCollection {
        get 
        {
            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows() ; row++) //GetNumberOfRows
                                                                      //will return 1000+ most of the time.
            {
                yield return wrapper.Evaluate("Water_Mains.col1");
                wrapper.RunCommand("Fetch Next From Water_Mains");
             }
        }
    }

public static List<String> MyListCollection
    {
        get
        {
            List<String> innerlist = new List<String>();

            wrapper.RunCommand("Fetch First From Water_Mains");
            for (int row = 0; row < tabinfo.GetNumberOfRows(); row++)
            {
                innerlist.Add(wrapper.Evaluate("Water_Mains.col1"));
                wrapper.RunCommand("Fetch Next From Water_Mains");
            }
            return innerlist;
        }
    }

次に、foreach各コレクションに対してループを使用します。

        foreach (var item in MyYieldCollection) //Same thing for MyListCollection.
        {
            Console.WriteLine(item);
        }

面白いのは、どういうわけかMyListCollection、MyYieldCollectionよりも速くループして完全に印刷できるように見えることです。

結果:

  • MyYieldCollection-> 2062
  • MyListCollection-> 1847

私はこれの理由を本当に見ることができません、私は何かを逃していますか、それともこれは正常ですか?

4

4 に答える 4

4

タイミングはどうしましたか?あなたはデバッガーにいますか?デバッグモードで?を使用しているようですDataTableので、コードをテスト リグのテンプレートとして使用し (毎回 1000 行を作成)、コマンド ラインでリリース モードで以下のようにハーネスを使用しました。結果は次のとおりです (括弧内の数字は、両方が同じ作業を行ったことを確認するためのチェックです)。

Yield: 2000 (5000000)
List: 2100 (5000000)

テスト ハーネス:

static  void Main()
{
    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count1 = 0;
    var watch1 = Stopwatch.StartNew();        
    for(int i = 0 ; i < 5000 ; i++) {
        foreach (var row in MyYieldCollection)
        {
            count1++;
        }
    }
    watch1.Stop();

    GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);
    int count2 = 0;
    var watch2 = Stopwatch.StartNew();
    for (int i = 0; i < 5000; i++)
    {
        foreach (var row in MyListCollection)
        {
            count2++;
        }
    }
    watch1.Stop();

    Console.WriteLine("Yield: {0} ({1})", watch1.ElapsedMilliseconds, count1);
    Console.WriteLine("List: {0} ({1})", watch2.ElapsedMilliseconds, count2);
}

(通常は を使用すべきではないことに注意してください。ただしGC.Collect、パフォーマンス テストのためにフィールドを平準化するために使用されます)

私が行った他の唯一の変更は、for繰り返しを避けるためにループに加えたことです。

int rows = tabinfo.Rows.Count;
for (int row = 0; row < rows; row++) {...}

だから私はあなたの数字を再現しません...

于 2008-12-24T10:33:49.103 に答える
1

ループの 1 回の反復にコストがかかり、コレクション内のいくつかのアイテムを反復するだけでよい場合はどうなるでしょうか?

yield を使用すると、得たものに対して支払うだけで済みます ;)

public IEnumerable<int> YieldInts()
{
    for (int i = 0; i < 1000; i++)
    {
        Thread.Sleep(1000) // or do some other work
        yield return i;
    }
}

public void Main()
{
    foreach(int i in YieldInts())
    {
        Console.WriteLine(i);
        if(i == 42)
        {
            break;
        }
    }
}
于 2008-12-24T02:16:37.470 に答える
0

私の推測では、JIT は、リストを返すバージョンの for ループをより適切に最適化できると思います。IEnumerable を返すバージョンでは、for ループで使用される行変数は、メソッドに対してのみローカルな変数ではなく、実際には生成されたクラスのメンバーになりました。

速度の差は約 10% にすぎないため、パフォーマンスが重要なコードでない限り、気にする必要はありません。

于 2008-12-24T02:37:08.160 に答える
-1

私が理解している限り、「yield return」は、実行する処理が実行されて関数/プロパティが終了し、塗りつぶされたIEnumarableが返されるまでループを続けます。つまり、foreachループ内の各項目に対して関数が呼び出されるのではなく、foreachループ内の何かが実行される前に一度だけ呼び出されます。

返されるコレクションのタイプによる可能性があります。おそらく、リストはIEnumerableのデータ構造よりも高速に繰り返すことができます。

于 2008-12-24T01:42:22.640 に答える