Original address: http://www.cnblogs.com/santian/p/4389675.html
For the yield keyword Let's look at the MSDN explanation first:
If you use in a statementThe yield keyword means that the method, operator, or get accessor in which it appears is an iterator. by using the yield definition iterator, you can implement the IEnumerable and IEnumerator modes of a custom collection type without additional explicit classes (classes that preserve the enumeration state, for example, see ienumerator<t>).
Yield is a grammatical sugar
The explanation of MSDN is always hard to understand. Actually the yield keyword is very well understood. First of all we have an understanding of the nature. Yield is a syntactic sugar. Since yield is a syntactic sugar in C #, it means that yield is a simplification of a complex behavior by simplifying a piece of code into a simple form that is easy for our programmers to use.
So what is yield in the end is the simplification of what behavior. Let's first look at the use of yield.
Let's take a look at the MSDN example.
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) { 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; for (int i = 0; i < exponent; i++) { result = result * number; yield return result; Yield return 3; Yield return 4; Yield return 5;}} }
This is a usage scenario for yield on MSDN.
Let's start by looking at the power method below. The static method returns a parameter of type ienumerablel<int>. According to our usual practice. You should perform a certain action on the data and return a parameter of type ienumerablel<int>. We have transformed the Power method as follows:
public static ienumerable<int> Power (int number, int exponent, string s) { int result = 1; Interface cannot be instantiated, we have a new one that implements the IEnumerable interface of the List 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 way of thinking. But there's a problem with that. This will be a new list, or any type that implements the IEnumerable interface. That's too much trouble. Be aware that IEnumerable is a common return type. Each use is a new list, or another type that implements the interface. Instead of using other types, we customize a type that implements the IEnumerable interface specifically for returning IEnumerable types. Our own customization is also very troublesome. So Microsoft helped us customize it. What this class is, that's the yield keyword, the syntactic sugar.
Syntactic sugar implementations (classes that implement the Ienumerable<t> interface)
Let's take a look at yield's anti-compilation code.
namespace consoleapplication2{using System; Using System.Collections; Using System.Collections.Generic; Using System.Diagnostics; Using System.Runtime.CompilerServices; Internal class Program {private static void Main (string[] args) {ienumerable<int> enum erable = Power (2, 8); Console.WriteLine ("Begin to iterate the collection."); foreach (int num in Power (2, 8)) {Console.Write ("{0}", num); } console.readkey (); 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__; } [compilergenerated] private sealed class <power>d__0:ienumerable<int>, IEnumerable, Ienumera Tor<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; [Debuggerhidden] public <power>d__0 (int <>1__state) {This.<>1__sta Te = <>1__state; This.<>l__initialthreadid = Environment.currentmanagedthreadid; } 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__state = 1; return true; Case 1:this.<>1__state =-1; This.<>2__current = 4; This.<>1__state = 2; return true; Case 2:this.<>1__state =-1; This.<>2__current = 5; This.<>1__state = 3; return true; Case 3:this.<>1__state =-1; Break } return false; } [Debuggerhidden] ienumerator<int> ienumerable<int>. GetEnumerator () {program.<power>d__0 d__; if ((Environment.currentmanagedthreadid = = This.<>l__initialthreadid) && (this.<>1__state = =-2)) {this.<>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__; } [Debuggerhidden] IEnumerator ienumerable.getenumerator () {return THIS.S Ystem. Collections.generic.ienumerable<system.int32>. GetEnumerator (); } [Debuggerhidden] void Ienumerator.reset () {throw new notsupportedexcept Ion (); } void IDisposable.Dispose () {} int ienumerator<int>. Current {[Debuggerhidden] get {return THIS.&L t;>2__current; }} object IEnumerator.Current {[Debuggerhidden] get {return this.<>2__current; } } } }}
The anti-compilation code has three parts, where the entry point of the program is private static void Main (string[] args) Power method public static ienumerable<int> power (int nu mber, int exponent) is the same as the code we wrote ourselves, but there is one more seal class in the decompile code.
private sealed class <power>d__0:ienumerable<int>, IEnumerable, Ienumerator<int>, IEnumerator, IDisposable
Now the situation is clear. Yield this syntactic sugar implements a class that implements the Ienumerable<int> interface to return the data we need to the ienumerable<int> type.
Let's take a look at the post-compilation power method
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__; }
It is now confirmed that the class that implements the enumeration interface is used to return the data type that we need.
Each yield return <expression>; adds a piece of data like an instance of the class. When yield is break, stop adding.
So the use of yield is clear. When we need to return to the IEnumerable type, direct yield returns the data. No new list, or other type. So yield is a typical syntactic sugar.
Special cases in use of yield
We see the compiler adding our yield data to a collection. The Power method instantiates a type that implements an enumeration interface in the compiler. But we write some methods in the power method, how the compiler will handle
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) {//The 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, "")) {C Onsole. 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"); } for (int i = 0; i < exponent; i++) {result = result * number; yield return result; } yield return 3; Yield return 4; Yield return 5; } }}
As we understand it, when we do var test = power (2, 8, ""), the Power method is called. The program should now print Console.WriteLine ("Begin to invoke GetItems () method"), and then proceed to 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 were running, we found
The printing order is different from what we imagined. At this point, it's time to look at the anti-compilation code.
namespace consoleapplication2{using System; Using System.Collections; Using System.Collections.Generic; Using System.Diagnostics; Using System.Runtime.CompilerServices; Internal class Program {private static void Main (string[] args) {ienumerable<int> enum erable = Power (2, 8, ""); Console.WriteLine ("Begin to iterate the collection."); foreach (int num in Power (2, 8, "")) {Console.Write ("{0}", num); } console.readkey (); } 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__; } [compilergenerated] private sealed class <power>d__0:ienumerable<int>, IEnumerable, Ienumera Tor<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; [Debuggerhidden] public <power>d__0 (int <>1__state) {This.<>1__sta Te = <>1__state; This.<>l__initialthreadid = Environment.currentmanagedthreadid; } 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 () metho D "); } this.<i>5__2 = 0; while (This.<i>5__2 < this.exponent) {This.<result>5__1 *= This.number; This.<>2__current = this.<result>5__1; This.<>1__state = 1; return true; Label_009d:this.<>1__state =-1; this.<i>5__2++; } this.<>2__current = 3; This.<>1__state = 2; return true; Case 1:goto label_009d; Case 2:this.<>1__state =-1; This.<>2__current = 4; This.<>1__state = 3; return true; Case 3:this.<>1__state =-1; This.<>2__current = 5; This.<>1__state = 4; return true; Case 4:this.<>1__state =-1; Break } return false; } [Debuggerhidden] ienumerator<int> ienumerable<int>. GetEnumerator () {program.<power>d__0 d__; if ((Environment.currentmanagedthreadid = = This.<>l__initialthreadid) && (this.<>1__state = =-2)) {this.<>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__; } [Debuggerhidden] IEnumerator ienumerable.getenumerator () {return THIS.S Ystem. Collections.generic.ienumerable<system.int32>. GetEnumerator (); } [Debuggerhidden] void Ienumerator.reset () {throw new notsupportedexcept Ion (); } void IDisposable.Dispose () {} int ienumerator<int>. Current {[Debuggerhidden] get {return THIS.&L t;>2__current; }} object IEnumerator.Current {[Debuggerhidden] get {return this.<>2__current; } } } }}
We see the Power method
public static ienumerable<int> Power (int number, int exponent, string s) { <power>d__0 d__ = new <p Ower>d__0 ( -2); D__.<>3__number = number; D__.<>3__exponent = exponent; d__.<>3__s = s; return d__; }
Or did we not add the printing method before the same. Our printing method does not appear in the power method, but is encapsulated into the class method that implements the enumeration interface in private bool MoveNext (). So the method is not executed immediately, but is executed when we use the data. If you do not understand this mechanism, there are other unexpected problems. For example, add some validators to the power method and throw an exception if the condition is not met. Such an exception check will not be performed. Only when we use the data will we execute it. This loses the sense of checking the data.
Specific examples can be seen Artech blogger's article
In addition, there are some caveats to using yield:
yield return or yield break statement in methods that has the Following characteristics: "> You cannot include yield return or in a method that has the following characteristics yield break statement:
anonymous method. For more information, see Anonymous Methods (C # Programming Guide).
The method that contains the unsafe block. For more information, see Unsafe (C # Reference).
Exception handling
The yield return statement cannot be placed in a try-catch block. The yield return statement can be placed in a try block of the try-finally statement.
The yield break statement can be in a try block or catch block, but not in a finally block.
If the foreach principal (outside the Iterator method) throws an exception, the finally block in the iterator method is executed .
This address: http://www.cnblogs.com/santian/p/4389675.html
Blog address: Two days a day, three days
Reprint please indicate the original source of the article in the form of hyperlink.
C # Basic small knowledge of yield keyword syntax sugar