2016-04-07 50 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);