You should always use yield when possible especially between layers, actually if it wasn’t for yield many features would be really hard to implement nowadays.
Consider the following piece of code:
public IEnumerable Dolist() { List nums = new List(); for (int i = 0; i < 5; i++) { nums.Add(i); } return nums; }
Looks so simple and straight forward, isn’t it? We iterate over 5 digits add them to a list and return them…
So straight forward… now compile your code to DLL and then disassemble it then check how the compiler compiled your code:
public class Class1
{
// Methods
public IEnumerable Dolist()
{
List nums = new List();
for (int i = 0; i < 5; i++)
{
nums.Add(i);
}
return nums;
}
}
Nothing strange here everything is as expected…
Now try this code :
public IEnumerable DoEnum()
{
for (int i = 0; i < 5; i++)
{
yield return i;
}
}
Now check the Compiled code:
public class Class1
{
// Methods
public IEnumerable DoEnum()
{
d__0 d__ = new d__0(-2);
d__.4__this = this;
return d__;
}
// Nested Types
[CompilerGenerated]
private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable
{
// Fields
private int 1__state;
private int 2__current;
public Class1 4__this;
private int l__initialThreadId;
public int 5__1;
// Methods
[DebuggerHidden]
public d__0(int 1__state)
{
this.1__state = 1__state;
this.l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
private bool MoveNext()
{
switch (this.1__state)
{
case 0:
this.1__state = -1;
this.5__1 = 0;
while (this.5__1 < 5)
{
this.2__current = this.5__1;
this.1__state = 1;
return true;
Label_0046:
this.1__state = -1;
this.5__1++;
}
break;
case 1:
goto Label_0046;
}
return false;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
if ((Thread.CurrentThread.ManagedThreadId == this.l__initialThreadId) && (this.1__state == -2))
{
this.1__state = 0;
return this;
}
Class1.d__0 d__ = new Class1.d__0(0);
d__.4__this = this.4__this;
return d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return this.System.Collections.Generic.IEnumerable.GetEnumerator();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
// Properties
int IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return this.2__current;
}
}
}
}
WHAT!????? So it’s not that you just don’t want to declare a variable to hold numbers! What is it then?
As you can see the .Net compiler would take your Code and compile it to a helper class which serves a State nested Class, to hold the state of every time you retrieve an item of the enumerator, which in other word you don’t get all the items at once! , it LAZY retrieve the items when you for each them which is perfect for LINQ! (isn’t that what LINQ is about?), so what! what advantage that would give me ?
Performance
Try the same implementation with 100 million numbers you will be amazed by the performance you get with a simple keyword (Yield), but guess what this is not all!
Yield return is one of the many keywords that Microsoft put into C# to support LINQ, part of the fact that LINQ is Lazy, let me put this into real example here and show you what you would gain from the Yield return keyword:
Consider this code:
public static class CustomerRepository { public static IEnumerable GetAllNotLazy() { List customers = new List(); for (int i = 0; i < 10; i++) { customers.Add(new Customer() { FirstName = "Mohammed", LastName = "Umar" }); } return customers; } public static IEnumerable GetAllLazy() { for (int i = 0; i x.FirstName == "Mohammed"); var Lazy = CustomerRepository.GetAllLazy().Where(x => x.FirstName == "Mohammed"); foreach (var customer in Lazy) { //do stuff } foreach (var customer in NotLazy) { //do stuff } }
With GetAllNoLazy all your objects would be retrieved at once!
With GetAllLazy your objects would be retrieved once you need them!
Imagine you were checking something or applying some filter while your iterating inside the for-each and you decided to break! With lazy you wouldn’t lose much, without being lazy all your objects would be in memory anyway despite the fact that you break or not!
I can go on and on with a situation where not using “Yield Return” would be a crime!
realy good work my bro thank you for posting than
and i think by using yield you can handle infinite loop and then
return first 100 item or skip 900 and get 10 item
Thank you bro. 🙂
Thanks Mohamed for great and detailed explanation 🙂
You`re more than welcome Radwan. 😀
Thank you ever so for you blog article. Great.
A big thank you for your post. Keep writing.
Simply Awesome Post !! Simple and Crisp !!! Thanks a ton.