Is yield useful outside of LINQ?

后端 未结 14 914
野性不改
野性不改 2020-12-24 06:44

When ever I think I can use the yield keyword, I take a step back and look at how it will impact my project. I always end up returning a collection instead of yeilding becau

相关标签:
14条回答
  • 2020-12-24 07:24

    Note that with yield, you are iterating over the collection once, but when you build a list, you'll be iterating over it twice.

    Take, for example, a filter iterator:

    IEnumerator<T>  Filter(this IEnumerator<T> coll, Func<T, bool> func)
    {
         foreach(T t in coll)
            if (func(t))  yield return t;
    }
    

    Now, you can chain this:

     MyColl.Filter(x=> x.id > 100).Filter(x => x.val < 200).Filter (etc)
    

    You method would be creating (and tossing) three lists. My method iterates over it just once.

    Also, when you return a collection, you are forcing a particular implementation on you users. An iterator is more generic.

    0 讨论(0)
  • 2020-12-24 07:24

    yield was developed for C#2 (before Linq in C#3).

    We used it heavily in a large enterprise C#2 web application when dealing with data access and heavily repeated calculations.

    Collections are great any time you have a few elements that you're going to hit multiple times.

    However in lots of data access scenarios you have large numbers of elements that you don't necessarily need to pass round in a great big collection.

    This is essentially what the SqlDataReader does - it's a forward only custom enumerator.

    What yield lets you do is quickly and with minimal code write your own custom enumerators.

    Everything yield does could be done in C#1 - it just took reams of code to do it.

    Linq really maximises the value of the yield behaviour, but it certainly isn't the only application.

    0 讨论(0)
  • 2020-12-24 07:25

    I've used yeild in non-linq code things like this (assuming functions do not live in same class):

    public IEnumerable<string> GetData()
    {
        foreach(String name in _someInternalDataCollection)
        {
            yield return name;
        }
    }
    
    ...
    
    public void DoSomething()
    {
        foreach(String value in GetData())
        {
            //... Do something with value that doesn't modify _someInternalDataCollection
        }
    }
    

    You have to be careful not to inadvertently modify the collection that your GetData() function is iterating over though, or it will throw an exception.

    0 讨论(0)
  • 2020-12-24 07:26

    Yield is useful because it saves you space. Most optimizations in programming makes a trade off between space (disk, memory, networking) and processing. Yield as a programming construct allows you to iterate over a collection many times in sequence without needing a separate copy of the collection for each iteration.

    consider this example:

    static IEnumerable<Person> GetAllPeople()
    {
        return new List<Person>()
        {
            new Person() { Name = "George", Surname = "Bush", City = "Washington" },
            new Person() { Name = "Abraham", Surname = "Lincoln", City = "Washington" },
            new Person() { Name = "Joe", Surname = "Average", City = "New York" }
        };
    }
    
    static IEnumerable<Person> GetPeopleFrom(this IEnumerable<Person> people,  string where)
    {
        foreach (var person in people)
        {
            if (person.City == where) yield return person;
        }
        yield break;
    }
    
    static IEnumerable<Person> GetPeopleWithInitial(this IEnumerable<Person> people, string initial)
    {
        foreach (var person in people)
        {
            if (person.Name.StartsWith(initial)) yield return person;
        }
        yield break;
    }
    
    static void Main(string[] args)
    {
        var people = GetAllPeople();
        foreach (var p in people.GetPeopleFrom("Washington"))
        {
            // do something with washingtonites
        }
    
        foreach (var p in people.GetPeopleWithInitial("G"))
        {
            // do something with people with initial G
        }
    
        foreach (var p in people.GetPeopleWithInitial("P").GetPeopleFrom("New York"))
        {
            // etc
        }
    }
    

    (Obviously you are not required to use yield with extension methods, it just creates a powerful paradigm to think about data.)

    As you can see, if you have a lot of these "filter" methods (but it can be any kind of method that does some work on a list of people) you can chain many of them together without requiring extra storage space for each step. This is one way of raising the programming language (C#) up to express your solutions better.

    The first side-effect of yield is that it delays execution of the filtering logic until you actually require it. If you therefore create a variable of type IEnumerable<> (with yields) but never iterate through it, you never execute the logic or consume the space which is a powerful and free optimization.

    The other side-effect is that yield operates on the lowest common collection interface (IEnumerable<>) which enables the creation of library-like code with wide applicability.

    0 讨论(0)
  • 2020-12-24 07:27

    I am a huge Yield fan in C#. This is especially true in large homegrown frameworks where often methods or properties return List that is a sub-set of another IEnumerable. The benefits that I see are:

    • the return value of a method that uses yield is immutable
    • you are only iterating over the list once
    • it a late or lazy execution variable, meaning the code to return the values are not executed until needed (though this can bite you if you dont know what your doing)
    • of the source list changes, you dont have to call to get another IEnumerable, you just iterate over IEnumeable again
    • many more

    One other HUGE benefit of yield is when your method potentially will return millions of values. So many that there is the potential of running out of memory just building the List before the method can even return it. With yield, the method can just create and return millions of values, and as long the caller also doesnt store every value. So its good for large scale data processing / aggregating operations

    0 讨论(0)
  • 2020-12-24 07:29

    The System.Linq IEnumerable extensions are great, but sometime you want more. For example, consider the following extension:

    public static class CollectionSampling
    {
        public static IEnumerable<T> Sample<T>(this IEnumerable<T> coll, int max)
        {
            var rand = new Random();
            using (var enumerator = coll.GetEnumerator());
            {
                while (enumerator.MoveNext())
                {
                    yield return enumerator.Current; 
                    int currentSample = rand.Next(max);
                    for (int i = 1; i <= currentSample; i++)
                        enumerator.MoveNext();
                }
            }
        }    
    }
    

    Another interesting advantage of yielding is that the caller cannot cast the return value to the original collection type and modify your internal collection

    0 讨论(0)
提交回复
热议问题