Improving coding efficiency in the special topics of Linq -- Article 3 enumeration classes you need to know:
As we all know, if a class can be enumerated, this class must implement the IEnumerable interface, while all of our linq classes are anonymous classes inherited from the IEnumerable interface,
Then the question is, how can IEnumerable enable these set types to be freely enumerated ???
I. Exploring IEnumerable
First, let's see what this interface defines, as shown in ILSpy:
From this interface, it seems that there is only one method of the IEnumerator interface type, and there is nothing that can be mined. In this case, you should be curious. Since foreach can enumerate Collection,
What is the relationship between the mechanism behind foreach and GetEnumerator ??? Let's write a demo and use ILDasm to check the IL behind it.
C # code:
static void Main(string[] args) { List<Action> list = new List<Action>(); foreach (var item in list) { Console.WriteLine(); } }
IL code:
.method private hidebysig static void Main(string[] args) cil managed{ .entrypoint // Code size 60 (0x3c) .maxstack 1 .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action> list, [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action> V_1, [2] class [mscorlib]System.Action item) IL_0000: nop IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::.ctor() IL_0006: stloc.0 IL_0007: nop IL_0008: ldloc.0 IL_0009: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action>::GetEnumerator() IL_000e: stloc.1 .try { IL_000f: br.s IL_0021 IL_0011: ldloca.s V_1 IL_0013: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::get_Current() IL_0018: stloc.2 IL_0019: nop IL_001a: call void [mscorlib]System.Console::WriteLine() IL_001f: nop IL_0020: nop IL_0021: ldloca.s V_1 IL_0023: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action>::MoveNext() IL_0028: brtrue.s IL_0011 IL_002a: leave.s IL_003b } // end .try finally { IL_002c: ldloca.s V_1 IL_002e: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class [mscorlib]System.Action> IL_0034: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0039: nop IL_003a: endfinally } // end handler IL_003b: ret} // end of method Program::Main
From the perspective of the red font marked by IL, the so-called foreach essentially calls the GetEnumerator () method of list to return an Enumerator Enumeration type, and then passes
Current gets the current value, and then uses MoveNext () to get the next value, and so on. If you restore IL, it is like the following:
var enumerator = list.GetEnumerator(); try { while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); } } finally { enumerator.Dispose(); }
At this time, do you have a strong desire to explore what GetEnumerator () has done and what role does MoveNext () play in it ??? Let's use ILSpy to check the List below.
The so-called Enumerator type...
1 [Serializable] 2 public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator 3 { 4 private List<T> list; 5 private int index; 6 private int version; 7 private T current; 8 [__DynamicallyInvokable] 9 public T Current10 {11 [__DynamicallyInvokable]12 get13 {14 return this.current;15 }16 }17 [__DynamicallyInvokable]18 object IEnumerator.Current19 {20 [__DynamicallyInvokable]21 get22 {23 if (this.index == 0 || this.index == this.list._size + 1)24 {25 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);26 }27 return this.Current;28 }29 }30 internal Enumerator(List<T> list)31 {32 this.list = list;33 this.index = 0;34 this.version = list._version;35 this.current = default(T);36 }37 [__DynamicallyInvokable]38 public void Dispose()39 {40 }41 [__DynamicallyInvokable]42 public bool MoveNext()43 {44 List<T> list = this.list;45 if (this.version == list._version && this.index < list._size)46 {47 this.current = list._items[this.index];48 this.index++;49 return true;50 }51 return this.MoveNextRare();52 }53 private bool MoveNextRare()54 {55 if (this.version != this.list._version)56 {57 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);58 }59 this.index = this.list._size + 1;60 this.current = default(T);61 return false;62 }63 [__DynamicallyInvokable]64 void IEnumerator.Reset()65 {66 if (this.version != this.list._version)67 {68 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);69 }70 this.index = 0;71 this.current = default(T);72 }73 }
By looking at the definition of the so-called Enumerator class, especially the red mark, you may be enlightened. In fact, the so-called enumeration class is just a packaging class of an enumeration set, for example, the List,
Then, the enumeration class uses index ++ to obtain the elements in the List one by one. That's all.
Ii. yield keywords
After you understand the so-called enumeration class, do you think of a weird yield lexical function? This can still be enumerated, for example, the following code:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 foreach (var item in Person.Run()) 6 { 7 Console.WriteLine(item); 8 } 9 10 }11 }12 13 class Person14 {15 public static IEnumerable<int> Run()16 {17 List<int> list = new List<int>();18 19 foreach (var item in list)20 {21 yield return item;22 }23 }24 }
So what exactly does yield do? In addition, it allows people to find out what is actually ??? Let's take a look at it using ILDasm.
Take a closer look at the code above. The so-called yield will generate an enumeration class for you, which is very similar to the Enumerator enumeration class in the List just now. If you understand the display
Enumerator of the enumeration class. I think this anonymous enumeration class Enumerator should be very simple.
Well, that's probably the case. With this foundation, I believe that the Anonymous Enumeration classes returned in linq will be okay for you ~~~