Yesterday I wrote "incorrect use of yield in WCF-errors that may be made by 99% of developers [Part 1]", which caused some discussion. The principle behind the syntax sugar about the yield keyword (C # compiler translates it into something) is actually quite simple. Although it sometimes causes some problems due to misuse, it is not at fault. Next, let's talk about yield that I understand in this short article.
Directory
I. Let's take a look at a simple example.
2. To understand the essence, you only need to check what yield is compiled.
Iii. Return to the WCF example
I. Let's take a look at a simple example.
Let's look at a simple example. We have compiled the following simple program in a Console application: The GetItems method with the return type IEnumerable <string> Returns a set of three strings in the form of yield return, at the beginning of the method, we print a text to indicate that the operation defined in the method starts to be executed. In the Main method, we first call the GetItems method to return the "collection object" and then call its ToArray method. Before calling this method, we print a text to show that the Set object is iterated.
static void Main(string[] args){ IEnumerable<string> items = GetItems(); Console.WriteLine("Begin to iterate the collection."); items.ToArray();}static IEnumerable<string> GetItems(){ Console.WriteLine("Begin to invoke GetItems() method"); yield return "Foo"; yield return "Bar"; yield return "Baz";}
For the above code, I think someone will think the result should be as follows:
Begin to invoke GetItems() methodBegin to iterate the collection.
But the following is the actual execution result. That is to say, once we use the yield return method to return the set element in a return type of IEnumerable or IEnumerable <T>, this means that the operation in the method will be "delayed" -- the actual execution of the operation does not occur when the method is called, but is postponed to iteration of the returned set. We can generally "Explain" this phenomenon in this way: Once we use yield return, the operation to return elements will be encapsulated into an executable expression to return, these expressions are executed only when the set is iterated.
Begin to iterate the collection.Begin to invoke GetItems() method
2. To understand the essence, you only need to check what yield is compiled.
The above explains yield return in the form of "delayed execution" and "executable expression", just to better understand the effect it shows. In fact, this is not the case, this is not the same as the delay loading of LINQ. Yield return is just a syntactic sugar of C # And is a little trick for compilers. How to see the essence through this layer of "sugar paper", you just need to look at the equivalent code after the compiler finally compiles. In the preceding example, no matter how the GetItems method returns the expected object, the returned value is always a type object that implements the IEnumerable <string> interface, we only need to look at the definition of this type to know if the C # compiler "explains" yield return.
We can directly use Reflector to open the compiled assembly, and then. the NET Framework version is adjusted to 1.0 (C # syntax sugar for later versions is not supported), so that you can view the code we have compiled in an "essential" way. As shown in the following code snippet, The GetItems method does not find the code we defined, but directly returns an object of the type <GetItems> d _ 0, I believe that readers know why there is no real text output when executing the GetItems method.
internal class Program{ private static IEnumerable<string> GetItems() { return new <GetItems>d__0(-2); } private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable}
<GetItems> d _ 0 is an automatically generated type. It implements the IEnumerable <string> interface and IEnumerator <string>, whose GetEnumerator () the method returns itself. For how to return specific elements during the iteration of the <GetItems> d _ 0 object, you just need to check the definition of this type. As shown in the following code snippet, the return implementation of the collection element is in the MoveNext () method, and the operations started by the method (Console. writeLine ("Begin to invoke GetItems () method") occurs during the first iteration.
private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable{ private int <>1__state; private string <>2__current; private bool MoveNext() { switch (this.<>1__state) { case 0: this.<>1__state = -1; Console.WriteLine("Begin to invoke GetItems() method"); this.<>2__current = "Foo"; this.<>1__state = 1; return true; case 1: this.<>1__state = -1; this.<>2__current = "Bar"; this.<>1__state = 2; return true; case 2: this.<>1__state = -1; this.<>2__current = "Baz"; this.<>1__state = 3; return true; case 3: this.<>1__state = -1; break; } return false; } string IEnumerator<string>.Current { [DebuggerHidden] get { return this.<>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return this.<>2__current; } }}
Iii. Return to the WCF example
Return to the example mentioned in "incorrect use of yield in WCF -- errors that may be made by developers in 99% [Part 1]", and explain why the following two sections of code are used, the exception thrown by the former cannot be processed normally by WCF, while the latter can. The reason is simple-the timing for throwing an exception is different for the two sections of code. For the latter, exceptions are immediately thrown when the GetItems method is executed, and WCF will capture this exception and handle it as an application-level exception. For the former, through the above analysis, we know that exceptions actually occur when an iteration is performed on the returned "set object. When is it? The exception thrown during serialization of the returned object will be treated as a system exception.
public class DemoService : IDemoService{ public IEnumerable<string> GetItems(string categoty) { if (string.IsNullOrEmpty(categoty)) { throw new FaultException("Invalid category"); } yield return "Foo"; yield return "Bar"; yield return "Baz"; }}public class DemoService : IDemoService{ public IEnumerable<string> GetItems(string categoty) { if (string.IsNullOrEmpty(categoty)) { throw new FaultException("Invalid category"); } return new string[] { "Foo", "Bar", "Baz" }; }}
I personally think this is something worth improving for WCF. But to avoid this problem, I recommend that you define the return type in the operation method of the WCF contract interface as an array, instead of IEnumerable or IEnumerable <T> (by the way, the serialization/deserialization behavior of WCF for Array, List, and other collection types is consistent ), however, I personally do not reject IEnumerable or IEnumerable <T>.
Incorrect use of yield in WCF-errors that may be made by 99% of developers [Part 1]
Incorrect use of yield in WCF-errors that may be made by 99% of developers [Part II]