对于集合的遍历,使用foreach是非常方便的,但是Emit动态生成foreach的代码就要复杂很多。它涉及到以下几个方面:
(1)IEnumerable<> 是所有可枚举类型的基础接口。
(2)IEnumerator<>,通过IEnumerable<> 接口的GetEnumerator方法可以获取枚举器IEnumerator<>,而对集合元素的遍历正是由IEnumerator<>的MoveNext方法完成的。
(3)遍历完成以后,需要调用IEnumerator<>的Dispose方法释放它。
(4)为了IEnumerator<>被正常释放,还需要使用try....finally块包含相应的代码。
下面我们来举个例子,比如对于如下的C#代码:
public
interface
ICompute
{
void Add( int a, int b);
}
public class Compute : ICompute
{
private ICollection < ICompute > computers;
public void Add( int a, int b)
{
foreach (ICompute com in computers)
{
com.Add(a, b);
}
}
}
{
void Add( int a, int b);
}
public class Compute : ICompute
{
private ICollection < ICompute > computers;
public void Add( int a, int b)
{
foreach (ICompute com in computers)
{
com.Add(a, b);
}
}
}
Compute类的Add方法使用了foreach进行遍历操作,我们可以将Add方法的等价形式写出来:
public
virtual
void
Add(
int
num1,
int
num2)
{
IEnumerator < ICompute > enumerator = this .computers.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
enumerator.Current.Add(num1, num2);
}
}
finally
{
if (enumerator != null )
{
enumerator.Dispose();
}
}
}
{
IEnumerator < ICompute > enumerator = this .computers.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
enumerator.Current.Add(num1, num2);
}
}
finally
{
if (enumerator != null )
{
enumerator.Dispose();
}
}
}
那么为了Emit类似的代码,需要生成如下的IL:
.method
public
hidebysig newslot
virtual
final instance
void
Add(int32 a, int32 b) cil managed
{
.maxstack 3
.locals init (
[ 0 ] class TheTest.ICompute com,
[ 1 ] class [mscorlib]System.Collections.Generic.IEnumerator` 1 < class TheTest.ICompute > CS$ 5 $ 0000 ,
[ 2 ] bool CS$ 4 $ 0001 )
L_0000: nop
L_0001: nop
L_0002: ldarg. 0
L_0003: ldfld class class [mscorlib]System.Collections.Generic.ICollection`1 < class TheTest.ICompute > TheTest.Compute::computers
L_000d: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator` 1 <! 0 > [mscorlib]System.Collections.Generic.IEnumerable` 1 < class TheTest.ICompute > ::GetEnumerator()
L_0012: stloc. 1
L_0013: br.s L_0027
L_0015: ldloc. 1
L_0016: callvirt instance ! 0 [mscorlib]System.Collections.Generic.IEnumerator` 1 < class TheTest.ICompute > ::get_Current()
L_001b: stloc. 0
L_001c: nop
L_001d: ldloc. 0
L_001e: ldarg. 1
L_001f: ldarg. 2
L_0020: callvirt instance void TheTest.ICompute::Add(int32, int32)
L_0025: nop
L_0026: nop
L_0027: ldloc. 1
L_0028: callvirt instance bool [mscorlib]System.Collections.IEnumerator:: MoveNext ()
L_002d: stloc. 2
L_002e: ldloc. 2
L_002f: brtrue.s L_0015
L_0031: leave.s L_0043
L_0033: ldloc. 1
L_0034: ldnull
L_0035: ceq
L_0037: stloc. 2
L_0038: ldloc. 2
L_0039: brtrue.s L_0042
L_003b: ldloc. 1
L_003c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0041: nop
L_0042: endfinally
L_0043: nop
L_0044: ret
. try L_0013 to L_0033 finally handler L_0033 to L_0043
}
{
.maxstack 3
.locals init (
[ 0 ] class TheTest.ICompute com,
[ 1 ] class [mscorlib]System.Collections.Generic.IEnumerator` 1 < class TheTest.ICompute > CS$ 5 $ 0000 ,
[ 2 ] bool CS$ 4 $ 0001 )
L_0000: nop
L_0001: nop
L_0002: ldarg. 0
L_0003: ldfld class class [mscorlib]System.Collections.Generic.ICollection`1 < class TheTest.ICompute > TheTest.Compute::computers
L_000d: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator` 1 <! 0 > [mscorlib]System.Collections.Generic.IEnumerable` 1 < class TheTest.ICompute > ::GetEnumerator()
L_0012: stloc. 1
L_0013: br.s L_0027
L_0015: ldloc. 1
L_0016: callvirt instance ! 0 [mscorlib]System.Collections.Generic.IEnumerator` 1 < class TheTest.ICompute > ::get_Current()
L_001b: stloc. 0
L_001c: nop
L_001d: ldloc. 0
L_001e: ldarg. 1
L_001f: ldarg. 2
L_0020: callvirt instance void TheTest.ICompute::Add(int32, int32)
L_0025: nop
L_0026: nop
L_0027: ldloc. 1
L_0028: callvirt instance bool [mscorlib]System.Collections.IEnumerator:: MoveNext ()
L_002d: stloc. 2
L_002e: ldloc. 2
L_002f: brtrue.s L_0015
L_0031: leave.s L_0043
L_0033: ldloc. 1
L_0034: ldnull
L_0035: ceq
L_0037: stloc. 2
L_0038: ldloc. 2
L_0039: brtrue.s L_0042
L_003b: ldloc. 1
L_003c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0041: nop
L_0042: endfinally
L_0043: nop
L_0044: ret
. try L_0013 to L_0033 finally handler L_0033 to L_0043
}
请注意红色代码部分,这与我们上面的描述是一致的。其它的IL代码的Emit相对简单,这里我们只提一下如何Emit最后这句try...finally块:
.
try
L_0013 to L_0033
finally
handler L_0033 to L_0043
对于try...catch...finnally块的标准Emit过程时这样的:
ILGenerator
methodGen = ..... ;
//
开始try块
methodGen.BeginExceptionBlock();
// ......
// 开始catch块
methodGen.BeginCatchBlock( typeof (Exception));
// ......
// 开始finally块
methodGen.BeginFinallyBlock();
// ......
// 结束try/Catch/finally块
methodGen.EndExceptionBlock();
methodGen.BeginExceptionBlock();
// ......
// 开始catch块
methodGen.BeginCatchBlock( typeof (Exception));
// ......
// 开始finally块
methodGen.BeginFinallyBlock();
// ......
// 结束try/Catch/finally块
methodGen.EndExceptionBlock();
如果只有try...catch...,则
ILGenerator
methodGen
=
..... ;
// 开始try块
methodGen.BeginExceptionBlock();
// ......
// 开始catch块
methodGen.BeginCatchBlock( typeof (Exception));
// ......
// 结束try/Catch块
methodGen.EndExceptionBlock();
// 开始try块
methodGen.BeginExceptionBlock();
// ......
// 开始catch块
methodGen.BeginCatchBlock( typeof (Exception));
// ......
// 结束try/Catch块
methodGen.EndExceptionBlock();
如果只有try...finnally...,则
ILGenerator
methodGen
=
..... ;
// 开始try块
methodGen.BeginExceptionBlock();
// ......
// 开始finally块
methodGen.BeginFinallyBlock();
// ......
// 结束try/finally块
methodGen.EndExceptionBlock();
// 开始try块
methodGen.BeginExceptionBlock();
// ......
// 开始finally块
methodGen.BeginFinallyBlock();
// ......
// 结束try/finally块
methodGen.EndExceptionBlock();