2013-05-08 13 views
7

Sto cercando di emettere un metodo che crea un'istanza di uno System.Lazy e non riuscendo con un errore PEVerify di "Token non valido", alla linea newobj instance void class [mscorlib]System.Lazy`1<class Example.ExpensiveType>::.ctor(class [mscorlib]System.Func`1<class Example.ExpensiveType>)Emit chiamata a System.Lazy <T> costruttore con Mono.Cecil

Guardando altrove con ILDASM, vedo che una chiamata corretta sarebbe simile a questa:

newobj  instance void class [mscorlib]System.Lazy`1<class Example.IHeater>::.ctor(class [mscorlib]System.Func`1<!0>) 

Purtroppo, io sono in perdita su come riprodurre questo con l'API Mono.Cecil. Qualcuno può aiutare con i generici?

Ecco quello che ho finora:

var get = new MethodDefinition(
      "Get", 
      MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, 
      ModuleDefinition.TypeSystem.Object); 

var funcType = new GenericInstanceType(ImportedTypes.FuncOfT); 
funcType.GenericArguments.Add(lazyElementType); 

var funcTypeCtor = new MethodReference(".ctor", ModuleDefinition.TypeSystem.Void, funcType); 
funcTypeCtor.Parameters.Add(new ParameterDefinition(ModuleDefinition.TypeSystem.Object)); 
funcTypeCtor.Parameters.Add(new ParameterDefinition(ModuleDefinition.TypeSystem.IntPtr)); 
funcTypeCtor.HasThis = true; 
funcTypeCtor = ModuleDefinition.Import(funcTypeCtor); 

var lazyTypeCtor = new MethodReference(".ctor", ModuleDefinition.TypeSystem.Void, lazyType); 
var parameterDefinition = new ParameterDefinition(funcType); 
lazyTypeCtor.Parameters.Add(parameterDefinition); 
lazyTypeCtor.HasThis = true; 
lazyTypeCtor = ModuleDefinition.Import(lazyTypeCtor); 

il = get.Body.GetILProcessor(); 
il.Emit(OpCodes.Ldarg_0); 
il.Emit(OpCodes.Ldftn, getTypedValue); 
il.Emit(OpCodes.Newobj, funcTypeCtor); 
il.Emit(OpCodes.Newobj, lazyTypeCtor); // This leads to the invalid token 
il.Emit(OpCodes.Ret); 
lazyBinding.Methods.Add(get); 

Qualsiasi aiuto sarebbe molto apprezzato - Sono perplesso!

risposta

9

Ho scoperto la risposta sepolta in un archivio di mailing list di anni (grazie a Gábor Kozár!). Non stavo creando/importando correttamente tipi generici e i loro metodi. Il codice che carica adeguatamente i tipi Lazy<T> e Func<T> segue:

var genericArgument = lazyElementType; 
var funcType = ModuleDefinition.Import(typeof(Func<>)).MakeGenericInstanceType(genericArgument); 
var funcCtor = 
    ModuleDefinition.Import(funcType.Resolve() 
            .Methods.First(m => m.IsConstructor && m.Parameters.Count == 2)) 
        .MakeHostInstanceGeneric(genericArgument); 

var lazyType = ModuleDefinition.Import(typeof(Lazy<>)).MakeGenericInstanceType(genericArgument); 
var lazyCtor = 
    ModuleDefinition.Import(lazyType.Resolve() 
            .GetConstructors() 
            .First(m => m.Parameters.Count == 1 
              && m.Parameters[0].ParameterType.Name.StartsWith("Func"))) 
        .MakeHostInstanceGeneric(genericArgument); 

// Method body as above 

Legenda quanto sopra è il metodo di estensione MakeHostInstanceGeneric, che è definito come

public static MethodReference MakeHostInstanceGeneric(
            this MethodReference self, 
            params TypeReference[] args) 
{ 
    var reference = new MethodReference(
     self.Name, 
     self.ReturnType, 
     self.DeclaringType.MakeGenericInstanceType(args)) 
    { 
     HasThis = self.HasThis, 
     ExplicitThis = self.ExplicitThis, 
     CallingConvention = self.CallingConvention 
    }; 

    foreach (var parameter in self.Parameters) { 
     reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); 
    } 

    foreach (var genericParam in self.GenericParameters) { 
     reference.GenericParameters.Add(new GenericParameter(genericParam.Name, reference)); 
    } 

    return reference; 
} 
Problemi correlati