在如何只能暴露IList<>的一个片段问题中,其中一个答案有以下代码片段:

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

yield关键字在这里做什么?我在一些地方看到过它,还有一个问题,但我还没搞清楚它到底是干什么的。我习惯于从一个线程屈服于另一个线程的意义上考虑屈服,但这在这里似乎无关紧要。


当前回答

关于Yield关键字的一个主要观点是惰性执行。现在我所说的惰性执行是指在需要时执行。更好的表达方式是举个例子

示例:不使用Yield,即没有Lazy Execution。

public static IEnumerable<int> CreateCollectionWithList()
{
    var list =  new List<int>();
    list.Add(10);
    list.Add(0);
    list.Add(1);
    list.Add(2);
    list.Add(20);

    return list;
}

示例:使用Yield,即惰性执行。

public static IEnumerable<int> CreateCollectionWithYield()
{
    yield return 10;
    for (int i = 0; i < 3; i++) 
    {
        yield return i;
    }

    yield return 20;
}

当我调用两个方法时。

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

你会注意到listItems里面有5个项目(调试时将鼠标悬停在listItems上)。 而yieldItems将只有一个对方法的引用,而不是对项目的引用。 这意味着它没有在方法中执行获取项的过程。一种只在需要时获取数据的非常有效的方法。 yield的实际实现可以在ORM中看到,如Entity Framework和NHibernate等。

其他回答

它产生了可枚举的序列。它所做的实际上是创建本地IEnumerable序列,并将其作为方法结果返回

迭代。它创建了一个“隐藏的”状态机,它会记住您在函数的每个额外循环中的位置,并从那里开始处理。

Yield return与枚举器一起使用。在yield语句的每次调用中,控制权都返回给调用方,但它确保被调用方的状态得到维护。因此,当调用方枚举下一个元素时,它将在yield语句之后立即在被调用方方法from语句中继续执行。

让我们通过一个例子来理解这一点。在这个例子中,对应于每一行,我已经提到了执行流的顺序。

static void Main(string[] args)
{
    foreach (int fib in Fibs(6))//1, 5
    {
        Console.WriteLine(fib + " ");//4, 10
    }            
}

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
    {
        yield return prevFib;//3, 9
        int newFib = prevFib + currFib;//6
        prevFib = currFib;//7
        currFib = newFib;//8
    }
}

此外,还为每个枚举维护状态。假设,我对Fibs()方法有另一个调用,那么它的状态将被重置。

关于Yield关键字的一个主要观点是惰性执行。现在我所说的惰性执行是指在需要时执行。更好的表达方式是举个例子

示例:不使用Yield,即没有Lazy Execution。

public static IEnumerable<int> CreateCollectionWithList()
{
    var list =  new List<int>();
    list.Add(10);
    list.Add(0);
    list.Add(1);
    list.Add(2);
    list.Add(20);

    return list;
}

示例:使用Yield,即惰性执行。

public static IEnumerable<int> CreateCollectionWithYield()
{
    yield return 10;
    for (int i = 0; i < 3; i++) 
    {
        yield return i;
    }

    yield return 20;
}

当我调用两个方法时。

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

你会注意到listItems里面有5个项目(调试时将鼠标悬停在listItems上)。 而yieldItems将只有一个对方法的引用,而不是对项目的引用。 这意味着它没有在方法中执行获取项的过程。一种只在需要时获取数据的非常有效的方法。 yield的实际实现可以在ORM中看到,如Entity Framework和NHibernate等。

屈服有两大用处,

它有助于在不创建临时集合的情况下提供自定义迭代。 它有助于进行有状态迭代。

为了更清楚地解释以上两点,我制作了一个简单的视频,你可以在这里观看