【Emit基础】如何发射foreach代码?

简介:   对于集合的遍历,使用foreach是非常方便的,但是Emit动态生成foreach的代码就要复杂很多。它涉及到以下几个方面: (1)IEnumerable 是所有可枚举类型的基础接口。 (2)IEnumerator,通过IEnumerable 接口的GetEnumerator方法可以获取枚举器IEnumerator,而对集合元素的遍历正是由IEnumerator的MoveNext方法完成的。

  对于集合的遍历,使用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);
            }
        }
    }

 

     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();
        }
    }
}

 

  那么为了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
}

 

   请注意红色代码部分,这与我们上面的描述是一致的。其它的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(); 

 

如果只有try...catch...,则

 ILGenerator  methodGen  =  ..... ;

 
// 开始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(); 

 

 

 

 

目录
相关文章
|
3月前
|
存储 编译器 C++
C++ 包装器—function
C++ 包装器—function
|
8月前
|
C++
【C++】function包装器
【C++】function包装器
26 0
|
4月前
|
存储 C++
【C++11特性篇】玩转C++11中的包装器(function&bind)
【C++11特性篇】玩转C++11中的包装器(function&bind)
|
4月前
|
JavaScript
Vue中子组件单个双向绑定发送事件使用input,多个使用 update:变量名;父组件接收时,v-model 接收单个,.sync 接收多个(vue3中使用v-model:name的写法)
Vue中子组件单个双向绑定发送事件使用input,多个使用 update:变量名;父组件接收时,v-model 接收单个,.sync 接收多个(vue3中使用v-model:name的写法)
|
6月前
|
缓存 数据处理 数据格式
Rxjs 里 Observable 对象的 tap 操作
Rxjs 里 Observable 对象的 tap 操作
25 0
|
7月前
|
API
Rxjs map, mergeMap 和 switchMap 的区别和联系
Rxjs map, mergeMap 和 switchMap 的区别和联系
50 1
|
9月前
Function过程
与内部函数一样,是一个可以反复使用的程序段,在其他程序段中均可以通过调用来执行这段程序,完成既定工作
|
JavaScript 前端开发 API
Js 异步处理演进,Callback=>Promise=>Observer
异步调用就像是接水管,相互缠绕的管道越多,就越容易漏水。如何将水管巧妙连通,使整个系统有足够的弹性,需要去认真思考 🤔 对于 JavaScript 异步的理解,不少人感到过困惑:Js 是单线程的,如何做到异步的呢?实际上,Js 引擎通过混用 2 种内存数据结构:栈和队列,来实现的。栈与队列的交互也就是大家所熟知的 Js 事件循环~~
|
缓存 JavaScript
computed 与 watch和 methods的异同之处
computed 与 watch和 methods的异同之处
80 0