2015-03-09 55 views
5

wygenerować następujący kod:Dlaczego ILGenerator wkładki Zostaw dyspozycję do foreach

public override void Map(IEnumerable enumerable1) 
{ 
    List<int> list = new List<int>(); 
    foreach (object obj2 in enumerable1) 
    { 
    } 
} 

thru Emit

Oto pełny kod:

MethodBuilder mapMethod = typeBuilder.DefineMethod("Map", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), new[] { typeof(IEnumerable) }); 

ILGenerator il = mapMethod.GetILGenerator(); 
LocalBuilder result = il.DeclareLocal(typeof(List<int>)); //0 
LocalBuilder item = il.DeclareLocal(typeof(object)); //1 
LocalBuilder enumeartor = il.DeclareLocal(typeof(IEnumerator)); //2 
LocalBuilder dispose = il.DeclareLocal(typeof(IDisposable)); //3 

Label labelWhile = il.DefineLabel(); 
Label labelReturn = il.DefineLabel(); 
Label labelMoveNext = il.DefineLabel(); 
Label labelEndFinally = il.DefineLabel(); 

//Create result List 
ConstructorInfo constructorInfo = (typeof(List<int>).GetConstructor(Type.EmptyTypes)); 
il.Emit(OpCodes.Newobj, constructorInfo); 
il.Emit(OpCodes.Stloc_0, result); 

il.Emit(OpCodes.Ldarg_1); 
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerable).GetMethod("GetEnumerator"), Type.EmptyTypes); 
il.Emit(OpCodes.Stloc_2, enumeartor); 

il.BeginExceptionBlock(); 
il.Emit(OpCodes.Br_S, labelMoveNext); 
il.MarkLabel(labelWhile); 

il.Emit(OpCodes.Ldloc_2); 
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetProperty("Current").GetGetMethod(), Type.EmptyTypes); 
il.Emit(OpCodes.Stloc_1, item); 
il.Emit(OpCodes.Ldloc_1); 

il.MarkLabel(labelMoveNext); 
il.Emit(OpCodes.Ldloc_2); 
il.EmitCall(OpCodes.Callvirt, typeof(IEnumerator).GetMethod("MoveNext"), Type.EmptyTypes); 
il.Emit(OpCodes.Brtrue_S, labelWhile); 

THE ISSUE IS HERE, I don't insert Leave instruction, but it there 
// il.Emit(OpCodes.Leave_S, labelReturn); 

il.BeginFinallyBlock(); 

il.Emit(OpCodes.Ldloc_2); 
il.Emit(OpCodes.Isinst, typeof(IDisposable)); 
il.Emit(OpCodes.Stloc_3, dispose); 
il.Emit(OpCodes.Ldloc_3); 
il.Emit(OpCodes.Brfalse_S, labelEndFinally); 

il.Emit(OpCodes.Ldloc_3); 
il.EmitCall(OpCodes.Callvirt, typeof(IDisposable).GetMethod("Dispose"), Type.EmptyTypes); 

il.MarkLabel(labelEndFinally); 
il.EndExceptionBlock(); 

il.MarkLabel(labelReturn); 
il.Emit(OpCodes.Ret); 

Oto wynik IL (patrz IL_001f):

.method public virtual instance void Map(class [mscorlib]System.Collections.IEnumerable A_1) cil managed 
{ 
    // Code size  54 (0x36) 
    .maxstack 5 
    .locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0, 
      object V_1, 
      class [mscorlib]System.Collections.IEnumerator V_2, 
      class [mscorlib]System.IDisposable V_3) 
    IL_0000: newobj  instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor() 
    IL_0005: stloc.0 
    IL_0006: ldarg.1 
    IL_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() 
    IL_000c: stloc.2 
    .try 
    { 
    IL_000d: br.s  IL_0017 
    IL_000f: ldloc.2 
    IL_0010: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() 
    IL_0015: stloc.1 
    IL_0016: ldloc.1 
    IL_0017: ldloc.2 
    IL_0018: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() 
    IL_001d: brtrue.s IL_000f 

    THE ISSUE IS HERE 
    IL_001f: leave  IL_0035 
    } // end .try 
    finally 
    { 
    IL_0024: ldloc.2 
    IL_0025: isinst  [mscorlib]System.IDisposable 
    IL_002a: stloc.3 
    IL_002b: ldloc.3 
    IL_002c: brfalse.s IL_0034 
    IL_002e: ldloc.3 
    IL_002f: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
    IL_0034: endfinally 
    } // end handler 
    IL_0035: ret 
} // end of method ForeachType::Map 

Czy możesz wyjaśnić, dlaczego pojawiła się instrukcja leave?

+1

Wierzę, że jest to wymóg IL do zakończenia jakiegokolwiek bloku 'try {}'. –

+0

Być może, ale ten sam ręczny kod ma instrukcję "leave.s". Jeśli wstawię "leave.s", mam dwie instrukcje: – GSerjo

+1

Nie jestem pewien, czy dokładnie rozumiem, co tu mówisz, ale "leave" i "leave.s" to ta sama instrukcja, z wyjątkiem dozwolonego zakresu celu etykieta. –

Odpowiedz

3

Hans Passant dzięki za poradę. Po code wyjaśniono, co się dzieje.

+0

Hmm, czy mógłbyś wyjaśnić, jak dla mnie kod jest poprawny, ale komentarz nie jest :) – GSerjo

+1

To właściwie niski blok kodu w ta metoda, która skomentowała '// generuj urlop dla klauzuli try ', która dodaje urlop, którego szukałeś - jak zauważył Ben, brak klauzuli" catch "w twoim kodzie oznacza, że ​​ten blok, skomentował" // generowanie urlopu dla jakakolwiek wcześniejsza klauzula catch "jest nieistotna. –

+0

oops, tak masz rację. Dzięki! – GSerjo