Mono.Cecil: call GENERIC base class' method from other assembly

最后都变了- 提交于 2019-11-29 14:46:57

问题


I'm following up on my earlier question: Mono.Cecil: call base class' method from other assembly.
I'm doing the same thing, but if my base class is generic it doesn't work.

//in Assembly A
class BaseVM<T> {}

//in Assembly B
class MyVM : Base<SomeModel> {
 [NotifyProperty]
 public string Something {get;set;}
}

It weaves the following code:

L_000e: call instance void [AssemblyA]Base`1::RaisePropertyChanged(string)

instead of

L_000e: call instance void [AssemblyA]Base`1<class SomeModel>::RaisePropertyChanged(string)

What is there to change?


回答1:


In your previous post you indicate that you're using code like:

TypeDefinition type = ...;
TypeDefintion baseType = type.BaseType.Resolve ();
MethodDefinition baseMethod = baseType.Methods.First (m => ...);
MethodReference baseMethodReference = type.Module.Import (baseMethod);
il.Emit (OpCodes.Call, baseMethodReference);

Obviously, this is not suitable for generics:

When you Resolve () the .BaseType, you're losing the generic instantiation information. You need to recreate the appropriate method call with the proper generic information from the base type.

To simplify things, let's use the following methods, taken from the Cecil test suite:

public static TypeReference MakeGenericType (this TypeReference self, params TypeReference [] arguments)
{
    if (self.GenericParameters.Count != arguments.Length)
        throw new ArgumentException ();

    var instance = new GenericInstanceType (self);
    foreach (var argument in arguments)
        instance.GenericArguments.Add (argument);

    return instance;
}

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

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

    foreach (var generic_parameter in self.GenericParameters)
        reference.GenericParameters.Add (new GenericParameter (generic_parameter.Name, reference));

    return reference;
}

With those, you can rewrite your code as:

TypeDefinition type = ...;
TypeDefintion baseTypeDefinition = type.BaseType.Resolve ();
MethodDefinition baseMethodDefinition = baseTypeDefinition.Methods.First (m => ...);
MethodReference baseMethodReference = type.Module.Import (baseMethodDefinition);
if (type.BaseType.IsGenericInstance) {
    var baseTypeInstance = (GenericInstanceType) type.BaseType;
    baseMethodReference = baseMethodReference.MakeGeneric (baseTypeInstance.GenericArguments.ToArray ());
}

il.Emit (OpCodes.Call, baseMethodReference);


来源:https://stackoverflow.com/questions/4968755/mono-cecil-call-generic-base-class-method-from-other-assembly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!