public EnumerationData(Type itemType) { EnumerableType = typeof(IEnumerable <>).MakeGenericType(itemType); EnumeratorType = typeof(IEnumerator <>).MakeGenericType(itemType); GetEnumerator = EnumerableType.GetMethod("GetEnumerator", new Type[0]); Current = EnumeratorType.GetProperty("Current"); }
private void EmitILForEachView(ILGenerator il, Action forEachAction) { Debug.Assert(il != null); Debug.Assert(forEachAction != null); // Declare the locals we need var viewLocal = il.DeclareLocal(_viewType); var enumeratorLocal = il.DeclareLocal(EnumerableType); var enumeratorContinueLocal = il.DeclareLocal(typeof(bool)); // Load the view instance on to the evaluation stack il.Emit(OpCodes.Ldarg, viewLocal.LocalIndex); // Call CompositeView<IViewType>.get_Views var getViews = GetCompositeViewViewsPropertyGetter(); il.EmitCall(OpCodes.Call, getViews, null); // Call IEnumerable<>.GetEnumerator var getViewsEnumerator = EnumerableType .GetMethod( "GetEnumerator", BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public); il.EmitCall(OpCodes.Callvirt, getViewsEnumerator, null); // Push the enumerator from the evaluation stack to the local variable il.Emit(OpCodes.Stloc, enumeratorLocal.LocalIndex); // Start a new exception block so that we can reliably dispose // the enumerator il.BeginExceptionBlock(); // Define some of the labels we need var moveNextLabel = il.DefineLabel(); var continueLabel = il.DefineLabel(); var endFinallyLabel = il.DefineLabel(); var exitLabel = il.DefineLabel(); // Skip straight ahead to moveNextLabel il.Emit(OpCodes.Br_S, moveNextLabel); // Mark this point with with continueLabel il.MarkLabel(continueLabel); // Push the enumerator on to the evaluation stack il.Emit(OpCodes.Ldloc, enumeratorLocal); // Call IEnumerator<>.get_Current on the enumerator var getCurrent = EnumeratorType.GetProperty("Current")?.GetGetMethod(); il.EmitCall(OpCodes.Callvirt, getCurrent, null); // Store the output from IEnumerator<>.get_Current into a local il.Emit(OpCodes.Stloc, viewLocal); // Push the view local back onto the evaluation stack il.Emit(OpCodes.Ldloc, viewLocal); // Push the incoming set value onto the evaluation stack il.Emit(OpCodes.Ldarg, 1); // Let the calling method inject some IL here forEachAction(); // Mark this point with the moveNextLabel il.MarkLabel(moveNextLabel); // Push the enumerator local back onto the evaluation stack il.Emit(OpCodes.Ldloc, enumeratorLocal); // Call IEnumerator.MoveNext on the enumerator var moveNext = typeof(IEnumerator).GetMethod("MoveNext"); il.EmitCall(OpCodes.Callvirt, moveNext, null); // Push the result of MoveNext from the evaluation stack to the local variable il.Emit(OpCodes.Stloc, enumeratorContinueLocal.LocalIndex); // Pull the result of MoveNext from the evaluation stack back onto the evaluation stack il.Emit(OpCodes.Ldloc, enumeratorContinueLocal.LocalIndex); // If MoveNext returned true, jump back to the continue label il.Emit(OpCodes.Brtrue_S, continueLabel); // Jump out of the try block il.Emit(OpCodes.Leave_S, exitLabel); // Start the finally block il.BeginFinallyBlock(); // Push the enumerator onto the evaluation stack, then compare against null il.Emit(OpCodes.Ldloc, enumeratorLocal); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); // Pop the comparison result into our local il.Emit(OpCodes.Stloc, enumeratorContinueLocal); // If the comparison result was true, jump to the end of the finally block il.Emit(OpCodes.Ldloc, enumeratorContinueLocal); il.Emit(OpCodes.Brtrue_S, endFinallyLabel); // Push the enumerator onto the evaluation stack il.Emit(OpCodes.Ldloc, enumeratorLocal); // Call IDisposable.Dispose var dispose = typeof(IDisposable).GetMethod("Dispose"); il.Emit(OpCodes.Callvirt, dispose); // Mark this point as exit point for our finally block il.MarkLabel(endFinallyLabel); // Close the try block il.EndExceptionBlock(); // Mark this point as our exit point (used to get out of the try block) il.MarkLabel(exitLabel); }