2016-04-07 37 views
1

我正在创建一个动态方法来在运行时复制一个对象。让我们假设:Reflection.Emit - IL - 对象上的调用方法

class Source 
{ 
    public List<int> L1 {get;set;} 
} 

class Dest 
{ 
    public List<int> L1 {get;set;} 
} 

现在,这种情况正常工作。我得到Source.L1并设置了Dest.L1。我这样做与以下IL:

 generator.Emit(OpCodes.Newobj, constructor); 
     generator.Emit(OpCodes.Dup); 
     generator.Emit(OpCodes.Ldarg_0); 
     generator.Emit(OpCodes.Callvirt, miGetter); 
     generator.Emit(OpCodes.Callvirt, miSetter); 
     generator.Emit(OpCodes.Ret); 

所有这一切工作正常......现在来棘手的部分。让我们改变目的地到:

class Dest 
{ 
    private List<int> _l1 = new List<int>(); 
    public List<int> L1 {get { return _l1; } } 
} 

现在我想在这种情况下,要做的就是调用Dest.L1.Clear(),然后Dest.L1.AddRange(...)。

我甚至无法使用.Clear工作。

我仍会有:

 generator.Emit(OpCodes.Newobj, constructor); 

     // this block is repeated 5 times for various properties 

     generator.Emit(OpCodes.Dup); 
     generator.Emit(OpCodes.Ldarg_0); 
     generator.Emit(OpCodes.Callvirt, miGetter); 
     generator.Emit(OpCodes.Callvirt, miSetter); 

     // List property will be copied here 
     // miGetter = Dest.L1.Get 
     // TODO 
     // end list property 

     generator.Emit(OpCodes.Ret); 

如何做,我需要建立IL在TODO块?我试图做dup/loadArg0 /调用miGetter /调用miClear,但那给了我一个无效的程序。

+0

你为什么要叫'Clear'?你不是在创造一个新的“Dest”吗?施工时这将是空的。 –

+0

看到较低的Dest的第二个版本。 L1现在是一个只读集合,所以你不能给调用者打电话(好吧,你可以,但你不应该) – SledgeHammer

+0

这不是我的问题。当你创建一个新的'Dest'时,'L1'列表是空的。你需要调用'AddRange',但是你不必先调用'Clear'。 –

回答

3

这里是发生了什么计算堆栈上的每一步的解释工作示例:

DynamicMethod method = 
    new DynamicMethod("Test", typeof(Dest), new Type[] { typeof(Source) }); 

var generator = method.GetILGenerator(); 

var constructor = typeof(Dest).GetConstructor(Type.EmptyTypes); 

var miGetter = typeof(Source).GetProperty("L1").GetMethod; 

var miDestGetter = typeof(Dest).GetProperty("L1").GetMethod; 

var addRange = typeof(List<int>).GetMethod("AddRange"); 

var clear = typeof(List<int>).GetMethod("Clear"); 

generator.Emit(OpCodes.Newobj, constructor);//Stack: DestObject 

generator.Emit(OpCodes.Dup);//Stack: DestObject,DestObject 

generator.Emit(OpCodes.Call, miDestGetter);//Stack: DestObject,DestObject.L1 

generator.Emit(OpCodes.Dup);//Stack: DestObject,DestObject.L1,DestObject.L1 

generator.Emit(OpCodes.Call, clear);//Stack: DestObject,DestObject.L1 

generator.Emit(OpCodes.Ldarg_0);//Stack: DestObject,DestObject.L1,SourceObject 

generator.Emit(OpCodes.Call, miGetter);//Stack: DestObject,DestObject.L1,SourceObject.L1 

generator.Emit(OpCodes.Call, addRange);//Stack: DestObject 

generator.Emit(OpCodes.Ret); 

var function = (Func<Source, Dest>)method.CreateDelegate(typeof(Func<Source, Dest>)); 

Source source = new Source 
{ 
    L1 = new List<int>() { 1, 2, 3 } 
}; 

var result = function(source);