Can someone demystify the yield keyword?

2019-01-21 11:04发布

I have seen the yield keyword being used quite a lot on Stack Overflow and blogs. I don't use LINQ. Can someone explain the yield keyword?

I know that similar questions exist. But none really explain what is its use in plain simple language.

9条回答
Lonely孤独者°
2楼-- · 2019-01-21 11:26

Let me add to all of this. Yield is not a keyword. It will only work if you use "yield return" other than that it will work like a normal variable.

It's uses to return iterator from a function. You can search further on that. I recommend searching for "Returning Array vs Iterator"

查看更多
Fickle 薄情
3楼-- · 2019-01-21 11:27

Take a look at the MSDN documentation and the example. It is essentially an easy way to create an iterator in C#.

public class List
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
查看更多
萌系小妹纸
4楼-- · 2019-01-21 11:28

I came up with this to overcome a .NET shortcoming having to manually deep copy List.

I use this:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

And at another place:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.

EDIT:

Better still, use a generic List cloner:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}
查看更多
干净又极端
5楼-- · 2019-01-21 11:30

The yield keyword is a convenient way to write an IEnumerator. For example:

public static IEnumerator<int> Range(int from, int to)
{
    for (int i = from; i < to; i++)
    {
        yield return i;
    }
}

is transformed by the C# compiler to something similiar to:

public static IEnumerator<int> Range(int from, int to)
{
    return new RangeEnumerator(from, to);
}

class RangeEnumerator : IEnumerator<int>
{
    private int from, to, current;

    public RangeEnumerator(int from, int to)
    {
        this.from = from;
        this.to = to;
        this.current = from;
    }

    public bool MoveNext()
    {
        this.current++;
        return this.current < this.to;
    }

    public int Current
    {
        get
        {
            return this.current;
        }
    }
}
查看更多
戒情不戒烟
6楼-- · 2019-01-21 11:32

In an effort to demystify I'll avoid talking about iterators, since they could be part of the mystery themselves.

the yield return and yield break statements are most often used to provide "deferred evaluation" of the collection.

What this means is that when you get the value of a method that uses yield return, the collection of things you are trying to get don't exist together yet (it's essentially empty). As you loop through them (using foreach) it will execute the method at that time and get the next element in the enumeration.

Certain properties and methods will cause the entire enumeration to be evaluated at once (such as "Count").

Here's a quick example of the difference between returning a collection and returning yield:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

// we're going to execute the GetYieldEnumerable() method
// but the foreach statement inside it isn't going to execute
var yieldNames = GetNamesEnumerable();

// now we're going to execute the GetList() method and
// the foreach method will execute
var listNames = GetList();

// now we want to look for a specific name in yieldNames.
// only the first two iterations of the foreach loop in the 
// GetYieldEnumeration() method will need to be called to find it.
if (yieldNames.Contains("Jim")
    Console.WriteLine("Found Jim and only had to loop twice!");

// now we'll look for a specific name in listNames.
// the entire names collection was already iterated over
// so we've already paid the initial cost of looping through that collection.
// now we're going to have to add two more loops to find it in the listNames
// collection.
if (listNames.Contains("Jim"))
    Console.WriteLine("Found Jim and had to loop 7 times! (5 for names and 2 for listNames)");

This can also be used if you need to get a reference to the Enumeration before the source data has values. For example if the names collection wasn't complete to start with:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

var yieldNames = GetNamesEnumerable();

var listNames = GetList();

// now we'll change the source data by renaming "Jim" to "Jimbo"
names[1] = "Jimbo";

if (yieldNames.Contains("Jimbo")
    Console.WriteLine("Found Jimbo!");

// Because this enumeration was evaluated completely before we changed "Jim"
// to "Jimbo" it isn't going to be found
if (listNames.Contains("Jimbo"))
    // this can't be true
else
   Console.WriteLine("Couldn't find Jimbo, because he wasn't there when I was evaluated.");
查看更多
可以哭但决不认输i
7楼-- · 2019-01-21 11:36

Eric White's series on functional programming it well worth the read in it's entirety, but the entry on Yield is as clear an explanation as I've seen.

查看更多
登录 后发表回答