yield return functionality

  • (2 Pages)
  • +
  • 1
  • 2

15 Replies - 876 Views - Last Post: 14 July 2012 - 06:12 AM Rate Topic: -----

#1 insanepenguin  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 238
  • Joined: 08-July 10

yield return functionality

Posted 13 July 2012 - 01:08 PM

Does this basically mean return item to the calling code?

e.g: In the code below the extension methods will work on anything that implements IEnumerable<Product>, iterate through the items and if the condition is true return a product item to the code that invoked the extension method (using yield return)?

Many thanks

{
    class Program
    {
        static void Main(string[] args)
        {
            ShoppingCart myCart = new ShoppingCart { products = new List<Product> { 
            new Product {Name = "Kayak", Price = 275M, Category = "Watersports"},
            new Product {Name = "Lifejacket", Price = 48.95M},
            new Product {Name = "Soccer ball", Price = 19.50M},
            new Product {Name = "Corner flag", Price = 34.95M},
            new Product {Name = "Liverpool Poster", Price = 5.0M}
            } };

            
            decimal total = myCart.TotalPrices();
            Console.WriteLine("Total amount in cart: {0:c}", total);

            foreach (Product prod in myCart.FilterByCategory("Watersports"))
            {
                Console.WriteLine("Matches found: {0} : {1}", prod.Category, prod.Name);
            }

            foreach (Product prod in myCart.FilterByPriceLower(50))
            {
                Console.WriteLine("Lower price: {1} Name: {0}", prod.Name, prod.Price);
            }

        }

    }

    public static class MyExtensionMethods
    {
        public static decimal TotalPrices(this IEnumerable<Product> cartParam)
        {
            decimal total = 0;

            foreach (Product prod in cartParam)
            {
                total += prod.Price;
            }
            return total;
        }

        public static IEnumerable<Product> FilterByCategory(this IEnumerable<Product> productEnum, string categoryParam)
        {
            foreach (Product prod in productEnum)
            {
                if (prod.Category == categoryParam)
                {
                    yield return prod;
                }
            }
        }

        public static IEnumerable<Product> FilterByPriceLower(this IEnumerable<Product> prodEnum, int price)
        {
            foreach (Product prod in prodEnum)
            {
                if (prod.Price < price)
                {
                    yield return prod;
                }
            }
        }
    }
    public class ShoppingCart : IEnumerable<Product>
    {
        public List<Product> products{ get; set;}

        public IEnumerator<Product> GetEnumerator()
        {
            return products.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }

    }
}



Is This A Good Question/Topic? 0
  • +

Replies To: yield return functionality

#2 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2112
  • View blog
  • Posts: 3,230
  • Joined: 21-June 11

Re: yield return functionality

Posted 13 July 2012 - 01:16 PM

Could you rephrase your question? The way you phrased your description it isn't clear what you think the difference between yield return and just return is - in fact it almost sounds as if you thought there was no difference.
Was This Post Helpful? 0
  • +
  • -

#3 insanepenguin  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 238
  • Joined: 08-July 10

Re: yield return functionality

Posted 13 July 2012 - 01:37 PM

Maybe a better way to phrase the question would be: What is the difference between return and yield return?

Thanks
Was This Post Helpful? 0
  • +
  • -

#4 CodingSup3rnatur@l-360  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 991
  • View blog
  • Posts: 971
  • Joined: 30-September 10

Re: yield return functionality

Posted 13 July 2012 - 02:02 PM

Here are some code snippets to illustrate the difference:

IEnumerable<int> CountTo10WithYieldReturn() {
    for (int i = 1; i <= 10; i++) {
         yield return i;
     }
}

IEnumerable<int> CountTo10WithReturn() {
     List<int> numbers = new List<int>();
     for (int i = 1; i <= 10; i++) {
          numbers.Add(i);
     }
     return numbers;
}



When the method with the return is called, the method creates a new list, and proceeds to populate that list with every number from 1 to 10 inclusive. Once every number has been generated, the complete list is returned.

When the method with the yield return is called, the method returns a compiler generated object that implements IEnumerable<int> (and IEnumerator<int>), of which is capable of producing numbers from 1 to 10 inclusive, but hasn't yet produced any of them. The first number is generated when the first call to MoveNext() is made on the returned object. MoveNext() is called when we iterate over the object, for example with a foreach loop.


Here is a basic implementation demonstrating how the yield return method works:

Spoiler


Any questions, just ask :)

This post has been edited by CodingSup3rnatur@l-360: 14 July 2012 - 04:11 AM

Was This Post Helpful? 0
  • +
  • -

#5 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2112
  • View blog
  • Posts: 3,230
  • Joined: 21-June 11

Re: yield return functionality

Posted 13 July 2012 - 02:04 PM

When you use yield return inside a method, it becomes an iterator. An iterator behaves differently from a regular method in the following ways:

When you do var x = f(); and f is a regular method, the code inside f will be executed until a return statement is reached. When the return statement is reached, the value given to return will be stored in the variable x. The value given to return must have the same type as the return type of the function (or be implicitly convertible).

The return type of an iterator must always be IEnumerable. When you do var x = f() and f is an iterator, no code will execute yet. Once you request an item from the IEnumerable (by iterating over it in a foreach loop or by manually using its IEnumerator), the code in f will execute up to the first yield return statement. The item you get back will be the value given to the yield return statement. The type of that value must be the type enumerated over (or implicitly convertible), e.g. if f's return type is IEnumerable<Foo> the value given to yield return must have type Foo (or be implicitly convertible to Foo). When you request the next item, the execution will resume where it left off and continue until a yield return is encountered again. When the end of the method is reached, the IEnumerable contains no further items and the next call to the enumerator's MoveNext method will return false.

For more information consult the MSDN documentation.
Was This Post Helpful? 0
  • +
  • -

#6 Momerath  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1010
  • View blog
  • Posts: 2,444
  • Joined: 04-October 09

Re: yield return functionality

Posted 13 July 2012 - 03:02 PM

yield return also allows us to build generators. Here is an example of a Fibonacci generator
using System;
using System.Collections.Generic;

namespace Testing {
    class Program {
        static void Main(string[] args) {
            // Generate 15 Fibonacci numbers
            int count = 0;
            foreach (int i in Fibonacci()) {
                Console.Write("{0} ", i);
                count++;
                if (count >= 15) break;
            }

            Console.WriteLine();
            Console.ReadLine();

        }

        static IEnumerable<int> Fibonacci() {
            int current = 1;
            int next = 1;
            while (true) {
                yield return current;
                int temp = current + next;
                current = next;
                next = temp;
            }
        }
    }
}


Note that there is no end to the Fibonacci generator, it will continue returning values forever. But, because of the yield return, it only generates the next number as needed. Of course it will overflow the size of an Int32 fairly quickly, but it is just an example :)
Was This Post Helpful? 0
  • +
  • -

#7 insanepenguin  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 238
  • Joined: 08-July 10

Re: yield return functionality

Posted 13 July 2012 - 03:23 PM

Thanks guys,

So using yield return can generate the objects required on the fly, for example the alternative to the FilterByCategory method below would have to populate a List<Products> (or something IEnumerable<Products>) then look for the filter?

    public static IEnumerable<Product> FilterByCategory(this IEnumerable<Product> productEnum, string categoryParam)
        {
            foreach (Product prod in productEnum)
            {
                if (prod.Category == categoryParam)
                {
                    yield return prod;
                }
            }
        }

       
    

    }
    }
    public class ShoppingCart : IEnumerable<Product>
    {
        public List<Product> products{ get; set;}

        public IEnumerator<Product> GetEnumerator()
        {
            return products.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }

    }
}


Was This Post Helpful? 0
  • +
  • -

#8 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2112
  • View blog
  • Posts: 3,230
  • Joined: 21-June 11

Re: yield return functionality

Posted 13 July 2012 - 03:26 PM

View PostMomerath, on 14 July 2012 - 12:02 AM, said:

yield return also allows us to build generators.


The way I understood it, "generator" is simply a word that other languages use to describe what C# calls an iterator. You make it sound as if it was something else. Did I misunderstand something?
Was This Post Helpful? 0
  • +
  • -

#9 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2112
  • View blog
  • Posts: 3,230
  • Joined: 21-June 11

Re: yield return functionality

Posted 13 July 2012 - 03:34 PM

View Postinsanepenguin, on 14 July 2012 - 12:23 AM, said:

So using yield return can generate the objects required on the fly, for example the alternative to the FilterByCategory method below would have to populate a List<Products> (or something IEnumerable<Products>) then look for the filter?


Yes - although I'm not entirely sure what you mean by "look for the filter". The list version of the code would do resultList.Add(prod); instead of yield return prod; and then return resultList; on the last line. Unlike the version using yield return, it would build the entire list in memory before returning control to the caller (so obviously it wouldn't work with infinite sequences).
Was This Post Helpful? 1
  • +
  • -

#10 AdamSpeight2008  Icon User is online

  • MrCupOfT
  • member icon


Reputation: 2262
  • View blog
  • Posts: 9,462
  • Joined: 29-May 08

Re: yield return functionality

Posted 13 July 2012 - 04:22 PM

The following sample may help you, they re-implement the basic functionality of the LINQ functions WHERE and SELECT.

public static Filter[T]( source : IEnumerable, filter : T -> bool ) : IEnumerable[T]
{
  foreach( item : T in source )
  {
    if( filter( item ) ) 
      {
        yield return item
      }
   }
}
public static Transform[T]( source : IEnumerable, tranform: T -> U) : IEnumerable[U]
{
  foreach( item : T in source )
  {
    yield return transform( item )
  }
}



And an of infinite sequences

public static TheyCanOnlyBeOnes() : IEnumerable[ int ]
{
  while ( True )
  {
    Yield 1
  }
}
public static Natural() : IEnumerable[ int ]
{
  var i = 0
  while i< int.MaxValue
  {
    i += 1
    yield i
  }

}



This post has been edited by AdamSpeight2008: 13 July 2012 - 04:26 PM

Was This Post Helpful? 0
  • +
  • -

#11 Momerath  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1010
  • View blog
  • Posts: 2,444
  • Joined: 04-October 09

Re: yield return functionality

Posted 14 July 2012 - 12:36 AM

View Postsepp2k, on 13 July 2012 - 03:26 PM, said:

The way I understood it, "generator" is simply a word that other languages use to describe what C# calls an iterator. You make it sound as if it was something else. Did I misunderstand something?
My understanding (and Wikipedia agrees with me, for whatever that is worth) and iterator traverses a collection (or container, as Wiki puts it). A generator isn't a collection as each value is, well, generated (calculated, etc.)
Was This Post Helpful? 0
  • +
  • -

#12 insanepenguin  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 238
  • Joined: 08-July 10

Re: yield return functionality

Posted 14 July 2012 - 12:53 AM

I've written a simple collection class to help me understand IEnumerable<T>.GetEnumerator();

Because my class is generic, implementing an iterator using IEnumerable<T>.GetEnumerator() allows the compiler to figure out behind the scenes how to iterate through different types using the BasicCollection<T> class? e.g:

namespace BasicEnumerator
{
    class Program
    {
        static void Main(string[] args)
        {
            BasicCollection<string> myCollection = new BasicCollection<string>();
            myCollection.AddData("Lets", "Try", "This", "Out");

            foreach (string item in myCollection)
            {
                Console.WriteLine(item);
            }
        }
    }

    class BasicCollection<T> : IEnumerable<T>
    {
        List<T> list = new List<T>();


        public void AddData(params T[] dataParam)
        {
            foreach (var data in dataParam)
            {
                list.Add(data);
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            foreach (var data in list)
            {
                yield return data;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

}



Thanks
Was This Post Helpful? 0
  • +
  • -

#13 CodingSup3rnatur@l-360  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 991
  • View blog
  • Posts: 971
  • Joined: 30-September 10

Re: yield return functionality

Posted 14 July 2012 - 04:10 AM

Basically, yes. The GetEnumerator() method of the IEnumerable<T> interface returns an iterator object that specifies how to iterate over the elements of the IEnumerable<T> object.

The yield return in your example tells the compiler to generate that iterator object that specifies how to iterate though an instance of BasicCollection<T>, and that iterator object is returned from GetEnumerator().

Your class will be something conceptually similar to this once compiled:

class BasicCollection<T> : IEnumerable<T> {
        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            return new BasicCollectionEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() { 
            throw new NotImplementedException(); 
        }

        //Compiler generated iterator class
        private sealed class BasicCollectionEnumerator : IEnumerator<T>, IEnumerator, IDisposable {
            //code specifying how to iterate through an instance of BasicCollection<T>...
        }
}


This post has been edited by CodingSup3rnatur@l-360: 14 July 2012 - 05:26 AM

Was This Post Helpful? 2
  • +
  • -

#14 insanepenguin  Icon User is offline

  • D.I.C Head

Reputation: 7
  • View blog
  • Posts: 238
  • Joined: 08-July 10

Re: yield return functionality

Posted 14 July 2012 - 04:43 AM

thanks CodingSup3rnatur@l-360,

That explanation helped a lot, so the yield result in GetEnumerator() is basically generating an iterator object in the background specifying to the calling code (foreach etc) how to iterate the collection<T>.
:D
Was This Post Helpful? 0
  • +
  • -

#15 CodingSup3rnatur@l-360  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 991
  • View blog
  • Posts: 971
  • Joined: 30-September 10

Re: yield return functionality

Posted 14 July 2012 - 05:09 AM

Yep, you've got it :).

Also, just to be absolutely accurate, the compiled GetEnumerator() method will actually look conceptually more like this in your example:

IEnumerator<T> IEnumerable<T>.GetEnumerator() {
    return new BasicCollectionEnumerator() { basicCollection = this };
}



The iterator class the compiler generates will have a field (of which I have named basicCollection) for holding the BasicCollection<T> instance you are trying to iterate through, and the extra bit of code above is just initializing that field.

It makes absolutely no difference to the concept (of which you have obviously grasped now). I'm just letting you know :)

This post has been edited by CodingSup3rnatur@l-360: 14 July 2012 - 05:12 AM

Was This Post Helpful? 1
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2