2016-04-07 2 views
1

Je travaille sur la création d'une méthode dynamique à l'exécution pour copier un objet. Supposons:Reflection.Emit - IL - méthode d'appel sur l'objet

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

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

Maintenant, cette situation fonctionne correctement. Je reçois Source.L1 et je définis Dest.L1. Je le fais avec le IL suivant:

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

Tout cela fonctionne très bien ... vient maintenant la partie difficile. Changeons Dest à:

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

Maintenant ce que je veux faire dans ce cas est d'appeler Dest.L1.Clear() puis Dest.L1.AddRange (...).

Je n'arrive même pas à faire fonctionner le .Clear.

j'aurai encore:

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

Comment dois-je mettre en place l'IL dans le bloc TODO? J'ai essayé de faire dup/loadArg0/appelez miGetter/appelez miClear, mais cela m'a donné un programme invalide.

+0

Pourquoi voulez-vous appeler 'clear'? n'êtes-vous pas en train de créer un nouveau 'Dest'? Ce serait vide lors de la construction. –

+0

Voir la 2ème version de Dest down lower. L1 est maintenant une collection en lecture seule, donc vous ne pouvez pas appeler le setter (bien, vous pouvez, mais vous ne devriez pas) – SledgeHammer

+0

Ce n'est pas ma question. Lorsque vous créez un nouveau 'Dest', la liste' L1' est vide. Vous devez appeler 'AddRange', mais vous n'avez pas besoin d'appeler' Clear' en premier. –

Répondre

3

Voici un exemple de travail avec une explication de ce qui se passe à la pile d'évaluation sur chaque étape:

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