Home>

Wrote yesterdayThe wrong use of yield in wcf-99%of developers can make mistakes [Part I]", Which caused some discussion.The principle behind the yield keyword (what the c#compiler translates it into) is actually quite simple,Although sometimes it can cause problems due to misuse,But it is not at fault.Next, let's talk briefly about yield as I understand it through this short article.

table of Contents

First, look at a simple example

Second, understand the essence,Just need to see what yield eventually compiles into

Third, return to the wcf example

First, look at a simple exampleLet's look at a simple example now.We wrote the following simple program in a console application:The method getitems with a return type of ienumerablereturns a set containing 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 execute.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 that iterates over the collection object.

static void main (string [] args)
{
 ienumerable<string>items=getitems ();
 console.writeline ("begin to iterate the collection.");
 items.toarray ();
}
</p>
<p>
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'm sure someone will think that the result should be like this:

begin to invoke getitems () method
begin to iterate the collection.

But the following is the actual execution result.That is,Once we return the collection element through yield return in a way with iienerable or iienerable,Means that this definition operation in the method will be "deferred"-the actual execution of the operation does not occur when the method is called,It is postponed until the returned collection is iterated.We can generally "explain" this phenomenon in such a way:once we use yield return, the operation of the return element will be encapsulated and returned as an "executable expression"Once we iterate over the collection,These expressions are executed.

begin to iterate the collection.
begin to invoke getitems () method

Second, understand the essence,Just need to see what yield eventually compiles into

Above we explained yield return in the form of "delayed execution" and "executable expression", just to better understand the effect it shows,Actually this is not the case,This is not the same thing as linq's lazy loading.yield return is just a syntactic sugar for c#,It's a little trick for the compiler.How to see the essence through this layer of "sugar paper"Just look at the equivalent code after the compiler finally compiles it.For the example above,Regardless of how the getitems method returns the required object,The return value is always an object of a certain type that implements the iunemerable<string>interface,We just need to look at the definition of this type to know if the C#compiler "interprets" yield return.

We can directly open the compiled assembly using reflector,Then adjust the version of the .net framework to 1.0 (the syntax sugar provided by c#for subsequent versions is not supported) so that you can view the code we wrote in an "essential" way.As shown in the following code snippet,The code we defined was not found in the getitems method,Instead, it directly returns an object of type "getitems" d__0. Seeing here, I believe that readers and friends know the real reason why there is no text output when the getitems method is executed.

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 ienumerableinterface and also implements the ienumerator, and its getenumerator () method actually returns itself.As for how to return specific elements when iterating over thed__0 object,Just look at the definition of the type at a glance.As shown in the following code snippet,The return of the collection element is implemented in the movenext () method. The operation started by the method (console.writeline ("begin to invoke getitems () method")) occurs at the first iteration.

private sealed class<getitems>d__0:ienumerable<string> ;, ienumerable, ienumerator<string> ;, ienumerator, idisposable
{
 private int<>1__state;
 private string<>2__current;
</p>
<p>
private bool movenext ()
 {
 switch (this.<>1__state)
 {
 case 0:
 this.<>1__state=-1;
 console.writeline ("begin to invoke getitems () method");
 this.<&2;2__current="foo";
 this.<&1;1__state=1;
 return true;
</p>
<p>
case 1:
 this.<>1__state=-1;
 this.<&2;2__current="bar";
 this.<&1;1__state=2;
 return true;
</p>
<p>
case 2:
 this.<>1__state=-1;
 this.<&2;2__current="baz";
 this.<&1;1__state=3;
 return true;
</p>
<p>
case 3:
 this.<>1__state=-1;
 break;
 }
 return false;
 }
 string ienumerator<string>.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
</p>
<p>
object ienumerator.current
 {
 [debuggerhidden]
 get
 {
 return this.<&2;2__current;
 }
 }
}

Third, return to the wcf example

Back to "The wrong use of yield in wcf-99%of developers can make mistakes [Part I]"Mentioned in the example,Now explain why for the following two pieces of code,The exception thrown by the former cannot be handled normally by wcf,The latter can.The reason is simple-the timing at which the two pieces of code throw exceptions is different.For the latter,The exception will be thrown immediately when the getitems method is executed,wcf will catch this exception and handle it normally as an application-level exception;For the former,From the above analysis, we know that the exception actually occurred when iterating over the returned "collection object".When exactly?In fact, when serializing the returned object,The exception thrown at this time will be handled 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";
 }
}
</p>
<p>
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 that wcf is worth improving,But to avoid such problems at present,I recommend defining the return type in the wcf contract interface operation method as an array.Instead of ienomerable or iienerable<t>(by the way,wcf's serialization/deserialization behavior is consistent for array, list, and other collection types), but I personally don't exclude iienerable or iienerable.

c
  • Previous jquery to achieve the secondary navigation slide down menu effect
  • Next Understanding of this keyword in js