views:

4185

answers:

9

The yield keyword is one of those keywords in C# that continues to mystify me and I've never been confident that I'm using it correctly.

Of the following two pieces of code, which is the preferred and why?

Version 1: Using yield return

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

Version 2: Return the list

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}
+3  A: 

And what about this?

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products;
    }
}

I guess this is much cleaner. I do not have VS2008 at hand to check, though. In any case, if Products implements IEnumerable (as it seems to - it is used in a foreach statement), I'd return it directly.

petr k.
Please edit OP to include more info instead of posting answers.
Brian Rasmussen
Well, you'd have to tell me what OP does exactly stand for :-) Thanks
petr k.
Original Post, I assume. I can't edit posts, so this seemed to be the way to go.
petr k.
+10  A: 

You can rewrite the first version as:

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        return db.Product;
    }
}

or possibly use:

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        return db.Product.Select(p => p);
    }
}

In the case of LINQ to SQL, it doesn't make very much difference because the results will all have been fetched anyway, I believe. In other cases, streaming the data makes all the difference between a solution being practical and not.

For more information on what yield does, by the way, read chapter 6 of C# in Depth, which is available for free on its Manning site. I also have a couple of articles about it:

Jon Skeet
+2  A: 

Assuming your products LINQ class uses a similar yield for enumerating/iterating, the first version is more efficient because its only yielding one value each time its iterated over.

The second example is converting the enumerator/iterator to a list with the ToList() method. This means it manually iterates over all the items in the enumerator and then returns a flat list.

Soviut
+15  A: 

I tend to use yield-return when I calculate the next item in the list (or even the next group of items).

Using your Version 2, you must have the complete list before returning. By using yield-return, you really only need to have the next item before returning.

Among other things, this helps spread the computational cost of complex calculations over a larger time-frame. For example, if the list is hooked up to a GUI and the user never goes to the last page, you never calculate the final items in the list.

Another case where yield-return is preferable is if the IEnumerable represents an infinite set. Consider the list of Prime Numbers, or an infinite list of random numbers. You can never return the full IEnumerable at once, so you use yield-return to return the list incrementally.

In your particular example, you have the full list of products, so I'd use Version 2.

abelenky
A: 

Return the list directly. Benefits:

  • It's more clear
  • The list is reusable. (the iterator is not) not actually true, Thanks Jon

You should use the iterator (yield) from when you think you probably won't have to iterate all the way to the end of the list, or when it has no end. For example, the client calling is going to be searching for the first product that satisfies some predicate, you might consider using the iterator, although that's a contrived example, and there are probably better ways to accomplish it. Basically, if you know in advance that the whole list will need to be calculated, just do it up front. If you think that it won't, then consider using the iterator version.

recursive
Don't forget that it's returning in IEnumerable<T>, not an IEnumerator<T> - you can call GetEnumerator again.
Jon Skeet
+3  A: 

The two pieces of code are really doing two different things. The first version will pull members as you need them. The second version will load all the results into memory before you start to do anything with it.

There's no right or wrong answer to this one. Which one is preferable just depends on the situation. For example, if there's a limit of time that you have to complete your query and you need to do something semi-complicated with the results, the second version could be preferable. But beware large resultsets, especially if you're running this code in 32-bit mode. I've been bitten by OutOfMemory exceptions several times when doing this method.

The key thing to keep in mind is this though: the differences are in efficiency. Thus, you should probably go with whichever one makes your code simpler and change it only after profiling.

Jason Baker
+1  A: 

This is kinda besides the point, but since the question is tagged best-practices I'll go ahead and throw in my two cents. For this type of thing I greatly prefer to make it into a property:

public static IEnumerable<Product> AllProducts
{
    get {
        using (AdventureWorksEntities db = new AdventureWorksEntities()) {
            var products = from product in db.Product
                           select product;

            return products;
        }
    }
}

Sure, it's a little more boiler-plate, but the code that uses this will look much cleaner:

prices = Whatever.AllProducts.Select (product => product.price);

vs

prices = Whatever.GetAllProducts().Select (product => product.price);

Note: I wouldn't do this for any methods that may take a while to do their work.

Mark A. Nicolosi
+1  A: 

This question (Is yield useful outside of LINQ?) asks a related question, you might want to look at it for an idea of how you can use yield.

Morten Christiansen
Thanks for the link to the PDF presentation. It does explain generators in a decent way.
beaudetious
+2  A: 

This is going to seem like a bizarre suggestion, but I learned how to use the yield keyword in C# by reading a presentation on generators in Python: David M. Beazley's http://www.dabeaz.com/generators/Generators.pdf. You don't need to know much Python to understand the presentation - I didn't. I found it very helpful in explaining not just how generators work but why you should care.

Robert Rossney