0

我一直在yield return用我写的一些代码来测试这个语句。我有两种方法:

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

有趣的是,出于某种原因,我似乎能够比MyListCollectionMyYieldCollection 更快地循环并打印出完整的内容。

结果:

  • MyYieldCollection -> 2062
  • 我的列表集合-> 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

如果循环的一次迭代很昂贵,而您只需要迭代集合中的几个项目,会发生什么?

有了收益,你只需要为你得到的东西付费;)

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 循环内的任何内容之前调用一次。

它可能是由返回的集合类型决定的。也许 List 可以比 IEnumerable 的任何数据结构更快地迭代。

于 2008-12-24T01:42:22.640 回答