views:

68

answers:

2

Hi,

I'm having a little trouble with a method in which I use yield return this doesn't work...

public IEnumerable<MyClass> SomeMethod(int aParam)
{
    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
        yield return new MyClass((int)row["Id"], (string)row["SomeString"]);
    }    
}

The above code never runs, when the call is made to this method it just steps over it.

However if I change to...

public IEnumerable<MyClass> SomeMethod(int aParam)
{
    IList<MyClass> classes = new List<MyClass>();

    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
         classes.Add(new MyClass((int)rows["Id"], (string)row["SomeString"]);
    }

    return classes;
}

It works just fine.

I don't understand why the first method never runs, could you help me in understanding what is happening here?

Thanks,
Dave

+3  A: 

The "yield" version is only "run" when the caller actually starts to enumerate the returned collection.

If, for instance, you only get the collection:

var results = SomeObject.SomeMethod (5);

and don't do anything with it, the SomeMethod will not execute.

Only when you start enumerating the results collection, it will hit.

foreach (MyClass c in results)
{
    /* Now it strikes */
}
Developer Art
Brilliant, exactly what I needed to know! Thank you
DaveParsons
A: 

yield return methods are actually converted into state machine classes that retrieve information lazily - only when you actually ask for it. That means that in order to actually pull data, you have to iterate over the result of your method.

// Gives you an iterator object that hasn't done anything yet
IEnumerable<MyClass> list = SomeMethod(); 

// Enumerate over the object
foreach (var item in list ) {
  // Only here will the data be retrieved. 
  // The method will stop on yield return every time the foreach loops.
}

The reason it runs in the second case is because there's no yield block, and thus the entire method runs in one go.

In this specific case, it's unlikely that you'll have any advantage to use an iterator block over a regular one because your GetClassesFromDb() isn't one either. This means that it will retrieve all the data at the same time first time it runs. Iterator blocks are best used when you can access items one at a time, because that way you can stop if you don't need them anymore.

Ilia Jerebtsov