Home>

For the yield keyword we first look at the explanation of msdn:

If you use the yield keyword in a statement, it means that the method, operator, or get accessor in which it appears is an iterator. By defining iterators using yield,Can implement custom collection typesienumerablewith ienumeratorNo other explicit classes are needed in the mode (classes that retain enumerated state,For an example,Seeienumerator<t>).

yield is a syntactic sugar

Seeing msdn's explanation is always blunt.Actually the yield keyword is easy to understand.First we have an understanding of nature.yield is a syntactic sugar.Since yield is a syntactic sugar in c#,Then it shows that yield is a simplification of a complex behavior,Is to simplify a piece of code into a simple form,Convenient for our programmers.

So what exactly is yield simplified?Let's first look at the usage scenario of yield.

Or look at the example on msdn.

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
</p>
<p>
namespace consoleapplication2
{
 class program
 {
 static void main (string [] args)
 {
 foreach (int i in power (2, 8, ""))
 {
 console.write ("{0}", i);
 }
 console.readkey ();
 }
</p>
<p>
 public static ienumerable<int>power (int number, int exponent, string s)
 {
 int result=1;
</p>
<p>
for (int i=0;i<exponent;i ++)
 {
 result=result * number;
 yield return result;
 }
 yield return 3;
 yield return 4;
 yield return 5;
 }
</p>
<p>
}
}

This is a use case for yield on msdn.

Let's first look at the following power method. The static method returns a parameter of type inumerablel.Follow our usual practice.Should perform some operations on the data,Then return a parameter of type ienumerablel.We transform the power method as follows:

public static ienumerable<int>power (int number, int exponent, string s)
 {
 int result=1;
 //Interface cannot be instantiated,Here we have a new list that implements the inumerable interface
 ienumerable<int>example=new list<int>();
 for (int i=0;i<exponent;i ++)
 {
 result=result * number;
 (example as list<int>). add (result);
 }
 return example;
 }

This is our usual thinking.But there is a problem with this.Here you need to create a new list, or any type that implements the inumerable interface.This is too much trouble.Know that inumerable is a commonly used return type.Each time you use a new list, or other types that implement the interface.Instead of using other types,It is better that we customize a type that implements the inumerable interface specifically to return iienerable types.Our own customization is also very troublesome.So Microsoft customized it for us.What is this class,That is the syntax sugar of the yield keyword.

Implementation of syntactic sugar

Let's take a look at the decompiled code for yield.

namespace consoleapplication2
{
 using system;
 using system.collections;
 using system.collections.generic;
 using system.diagnostics;
 using system.runtime.compilerservices;
</p>
<p>
internal class program
 {
 private static void main (string [] args)
 {
 ienumerable<int>enumerable=power (2, 8);
 console.writeline ("begin to iterate the collection.");
 foreach (int num in power (2, 8))
 {
 console.write ("{0}", num);
 }
 console.readkey ();
 }
</p>
<p>
public static ienumerable<int>power (int number, int exponent)
 {
 <power>d__0 d__=new<power>d__0 (-2);
 d __.<>3__number=number;
 d __.<>3__exponent=exponent;
 return d__;
 }
</p>
<p>
[compilergenerated]
 private sealed class<power>d__0:ienumerable<int> ;, ienumerable, ienumerator<int> ;, ienumerator, idisposable
 {
 private int<>1__state;
 private int<>2__current;
 public int<>3__exponent;
 public int<>3__number;
 private int<<l__initialthreadid;
 public int<result>5__1;
 public int exponent;
 public int number;
</p>
<p>
[debuggerhidden]
 public<power>d__0 (int<>1__state)
 {
 this.<>1__state =<>1__state;
 this.<&l;l__initialthreadid=environment.currentmanagedthreadid;
 }
</p>
<p>
private bool movenext ()
 {
 switch (this.<>1__state)
 {
 case 0:
 this.<>1__state=-1;
 this.<result>5__1=1;
 console.writeline ("begin to invoke getitems () method");
 this.<>2__current=3;
 this.<&1;1__state=1;
 return true;
</p>
<p>
case 1:
 this.<>1__state=-1;
 this.<>2__current=4;
 this.<&1;1__state=2;
 return true;
</p>
<p>
case 2:
 this.<>1__state=-1;
 this.<>2__current=5;
 this.<&1;1__state=3;
 return true;
</p>
<p>
case 3:
 this.<>1__state=-1;
 break;
 }
 return false;
 }
</p>
<p>
[debuggerhidden]
 ienumerator<int>ienumerable<int>.getenumerator ()
 {
 program.<power>d__0 d__;
 if ((environment.currentmanagedthreadid == this.<>l__initialthreadid)&&(this.<>1__state == -2))
 {
 this.<&1;1__state=0;
 d__=this;
 }
 else
 {
 d__=new program.<power>d__0 (0);
 }
 d __. number=this.<>3__number;
 d __. exponent=this.<>3__exponent;
 return d__;
 }
</p>
<p>
[debuggerhidden]
 ienumerator ienumerable.getenumerator ()
 {
 return this.system.collections.generic.ienumerable<system.int32>.getenumerator ();
 }
</p>
<p>
[debuggerhidden]
 void ienumerator.reset ()
 {
 throw new notsupportedexception ();
 }
</p>
<p>
void idisposable.dispose ()
 {
 }
</p>
<p>
int ienumerator<int>.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
</p>
<p>
object ienumerator.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
 }
 }
}

The decompiled code has three parts,The entry point of the program private static void main (string [] args) power method public static ienumerablepower (int number, int exponent) is the same as the code we wrote ourselves,But there is one more sealed class in the decompiled code

private sealed class<power>d__0:ienumerable<int> ;, ienumerable, ienumerator<int> ;, ienumerator, idisposable

The situation is now clear.Yield syntactic sugar implements a class that implements the ienumerableinterface to return the data we need to type ienumerable.

Let's take a look at the power method after decompilation

public static ienumerable<int>power (int number, int exponent)
 {
 <power>d__0 d__=new<power>d__0 (-2);
 d __.<>3__number=number;
 d __.<>3__exponent=exponent;
 return d__;
 }

Confirm at this time,It is indeed a class that implements the enumeration interface to return the data type we need.

Each time yield return<expression>will add a piece of data to the instance of the class.Stop adding when yield break ;.

The usage of yield is now clear.When we need to return iienumable type,Just yield the data directly.Neither need to create a new list, or other types.So yield is a typical syntactic sugar.

Special cases in yield use

We see that the compiler has added our yield data to a collection.The power method instantiates a type in the compiler that implements the enumeration interface.But we write some methods in the power method,What the compiler will do

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading.tasks;
namespace consoleapplication2
{
 class program
 {
 static void main (string [] args)
 {
 //Method is called here.
 var test=power (2, 8, "");
 console.writeline ("begin to iterate the collection.");
 //display powers of 2 up to the exponent of 8:
 foreach (int i in power (2, 8, ""))
 {
 console.write ("{0}", i);
 }
 console.readkey ();
 }
 public static ienumerable<int>power (int number, int exponent, string s)
 {
 int result=1;
 if (string.isnullorempty (s))
 {
 //throw new exception ("This is an exception");
 console.writeline ("begin to invoke getitems () method");
 }
</p>
<p>
for (int i=0;i<exponent;i ++)
 {
 result=result * number;
 yield return result;
 }
 yield return 3;
 yield return 4;
 yield return 5;
 }
 }
}

According to our understanding, when we var test=power (2, 8, "") ;, the power method is indeed called. At this point the program should print the console.writeline ("begin to invoke getitems () method");and then continue to execute the console.writeline ("begin to iterate the collection.");Method. So the print order should be

begin to invoke getitems () method

begin to iterate the collection.

But when we run, we find

The print order is different than we think.Still look at the decompiled code.

namespace consoleapplication2
{
 using system;
 using system.collections;
 using system.collections.generic;
 using system.diagnostics;
 using system.runtime.compilerservices;
</p>
<p>
internal class program
 {
 private static void main (string [] args)
 {
 ienumerable<int>enumerable=power (2, 8, "");
 console.writeline ("begin to iterate the collection.");
 foreach (int num in power (2, 8, ""))
 {
 console.write ("{0}", num);
 }
 console.readkey ();
 }
</p>
<p>
public static ienumerable<int>power (int number, int exponent, string s)
 {
 <power>d__0 d__=new<power>d__0 (-2);
 d __.<>3__number=number;
 d __.<>3__exponent=exponent;
 d __.<>3__s=s;
 return d__;
 }
</p>
<p>
[compilergenerated]
 private sealed class<power>d__0:ienumerable<int> ;, ienumerable, ienumerator<int> ;, ienumerator, idisposable
 {
 private int<>1__state;
 private int<>2__current;
 public int<>3__exponent;
 public int<>3__number;
 public string<>3__s;
 private int<<l__initialthreadid;
 public int<i>5__2;
 public int<result>5__1;
 public int exponent;
 public int number;
 public string s;
</p>
<p>
[debuggerhidden]
 public<power>d__0 (int<>1__state)
 {
 this.<>1__state =<>1__state;
 this.<&l;l__initialthreadid=environment.currentmanagedthreadid;
 }
</p>
<p>
private bool movenext ()
 {
 switch (this.<>1__state)
 {
 case 0:
 this.<>1__state=-1;
 this.<result>5__1=1;
 if (string.isnullorempty (this.s))
 {
 console.writeline ("begin to invoke getitems () method");
 }
 this.<i>5__2=0;
 while (this.<i>5__2<this.exponent)
 {
 this.<result>5__1 *=this.number;
 this.<&2;2__current=this.<result>5__1;
 this.<&1;1__state=1;
 return true;
 label_009d:
 this.<>1__state=-1;
 this.<i>5__2 ++;
 }
 this.<>2__current=3;
 this.<&1;1__state=2;
 return true;
</p>
<p>
case 1:
 goto label_009d;
</p>
<p>
case 2:
 this.<>1__state=-1;
 this.<>2__current=4;
 this.<&1;1__state=3;
 return true;
</p>
<p>
case 3:
 this.<>1__state=-1;
 this.<>2__current=5;
 this.<>1__state=4;
 return true;
</p>
<p>
case 4:
 this.<>1__state=-1;
 break;
 }
 return false;
 }
</p>
<p>
[debuggerhidden]
 ienumerator<int>ienumerable<int>.getenumerator ()
 {
 program.<power>d__0 d__;
 if ((environment.currentmanagedthreadid == this.<>l__initialthreadid)&&(this.<>1__state == -2))
 {
 this.<&1;1__state=0;
 d__=this;
 }
 else
 {
 d__=new program.<power>d__0 (0);
 }
 d __. number=this.<>3__number;
 d __. exponent=this.<>3__exponent;
 d __. s=this.<>3__s;
 return d__;
 }
</p>
<p>
[debuggerhidden]
 ienumerator ienumerable.getenumerator ()
 {
 return this.system.collections.generic.ienumerable<system.int32>.getenumerator ();
 }
</p>
<p>
[debuggerhidden]
 void ienumerator.reset ()
 {
 throw new notsupportedexception ();
 }
</p>
<p>
void idisposable.dispose ()
 {
 }
</p>
<p>
int ienumerator<int>.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
</p>
<p>
object ienumerator.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
 }
 }
}

We see the power method

public static ienumerable<int>power (int number, int exponent, string s)
 {
 <power>d__0 d__=new<power>d__0 (-2);
 d __.<>3__number=number;
 d __.<>3__exponent=exponent;
 d __.<>3__s=s;
 return d__;
 }

It's still the same as before before we added the printing method.Our print method does not appear in the power method, but is encapsulated into a class method private bool movenext () that implements the enum interface. So the method is not executed immediately,It is executed when we use data.If you do n’t understand this mechanism,It is prone to other unexpected problems.For example, add some verification procedures to the power method,If the conditions are not met, an exception is thrown.Such exception checking will not be performed.It only executes when we use data.This loses the meaning of examining the data.

Specific examples can be seen in the blog post by artech

There are some other considerations when using yield:

You cannot include yield return or yield break statements in methods that:

Anonymous method. For more information,See anonymous methods (c#programming guide).

Methods that contain unsafe blocks. For more information,See unsafe (c#reference).

Exception handling

You cannot place a yield return statement in a try-catch block. You can place a yield return statement in the try block of a try-finally statement.

A yield break statement can be in a try block or a catch block, but not in a finally block.

If the foreach body (outside of the iterator method) throws an exception,The finally block in the iterator method is executed.

c
  • Previous C # method for connecting to the database and updating the database
  • Next Pure javascript to determine whether the query date is a valid date