使用yield return时方法未被调用

9

我在使用一个方法时遇到了一些问题,其中我使用了yield return,但是这并没有起作用...

public IEnumerable<MyClass> SomeMethod(int aParam)
{
    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
        yield return new MyClass((int)row["Id"], (string)row["SomeString"]);
    }    
}

上面的代码永远不会运行,当调用这个方法时,它只是跳过了它。
然而,如果我改成...
public IEnumerable<MyClass> SomeMethod(int aParam)
{
    IList<MyClass> classes = new List<MyClass>();

    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
         classes.Add(new MyClass((int)rows["Id"], (string)row["SomeString"]);
    }

    return classes;
}

它完全正常运行。

我不明白为什么第一种方法从来没有运行过,你能帮助我理解这里发生了什么吗?


在构造函数中这样调用:Prop = SomeMethod(param); - user61470
3个回答

9
“yield”版本只有在调用者开始枚举返回的集合时才会“运行”。
例如,如果您只获取集合:
var results = SomeObject.SomeMethod (5);

如果你只是获取了SomeMethod的结果而没有对其进行任何操作,它将不会被执行。

只有当你开始枚举results集合时,它才会被触发。

foreach (MyClass c in results)
{
    /* Now it strikes */
}

2
< p > yield return 方法实际上被转换为状态机类,以惰性方式检索信息 - 只有在您实际请求时才会检索。这意味着为了实际获取数据,您必须遍历方法的结果。

// Gives you an iterator object that hasn't done anything yet
IEnumerable<MyClass> list = SomeMethod(); 

// Enumerate over the object
foreach (var item in list ) {
  // Only here will the data be retrieved. 
  // The method will stop on yield return every time the foreach loops.
}

在第二种情况下,它能够运行的原因是没有yield块,因此整个方法一次性运行。在这种特定情况下,使用迭代器块与普通块相比不太可能有任何优势,因为您的GetClassesFromDb()也不是其中之一。这意味着它将在第一次运行时同时检索所有数据。当您可以逐个访问项目时,最好使用迭代器块,因为这样您可以在不再需要它们时停止。

0

我不得不通过一种近乎灾难性的方式学会了yield关键字是多么的酷和危险,当我决定使我们公司的解析器懒惰地读取传入的数据时。幸运的是,我们实现的功能中只有少数一个使用了yield关键字。花了几天时间才意识到它完全没有工作。

yield关键字会尽可能地变得懒惰,甚至跳过整个方法,如果你不用像.ToList().FirstOrDefault().Any()这样的东西去工作它。

以下是两个变体,一个使用该关键字,另一个返回一个纯列表。其中一个甚至不会费心去执行,而另一个会,尽管它们看起来是相同的。

public class WhatDoesYieldDo
{
    public List<string> YieldTestResults;

    public List<string> ListTestResults;

    [TestMethod]
    public void TestMethod1()
    {
        ListTest();
        Assert.IsTrue(ListTestResults.Any());

        YieldTest();
        Assert.IsTrue(YieldTestResults.Any());
    }

    public IEnumerable<string> YieldTest()
    {
        YieldTestResults = new List<string>();
        for (var i = 0; i < 10; i++)
        {
            YieldTestResults.Add(i.ToString(CultureInfo.InvariantCulture));
            yield return i.ToString(CultureInfo.InvariantCulture);
        }
    }

    public IEnumerable<string> ListTest()
    {
        ListTestResults = new List<string>();

        for (var i = 0; i < 10; i++)
        {
            ListTestResults.Add(i.ToString(CultureInfo.InvariantCulture));
        }

        return ListTestResults;
    }
}

故事的寓意是:确保如果有一个返回IEnumerable的方法,并且在该方法中使用了yield,那么你必须有一些东西来迭代结果,否则该方法将根本不会执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接