Mono.Cecil:从其他程序集中调用GENERIC基类的方法 [英] Mono.Cecil: call GENERIC base class' method from other assembly

查看:157
本文介绍了Mono.Cecil:从其他程序集中调用GENERIC基类的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在跟进我之前的问题: Mono .Cecil:从其他程序集调用基类'方法
我正在做同样的事情,但是如果我的基类是通用的,它就不起作用。

  //在汇编A 
class BaseVM< T> {}

//在汇编B
class MyVM:Base< SomeModel> {
[NotifyProperty]
public string $ {
$ b $ $ $ $ $ b

它编织以下代码:

  L_000e:调用实例void [AssemblyA] Base`1 :: RaisePropertyChanged(字符串)

而不是

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

有什么要改变的?

解决方案

在上一篇文章中,您表明您使用的是代码例如:

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

显然,这不适合泛型:

Resolve() .BaseType时,您正在丢失泛型实例化信息。您需要使用基本类型中正确的通用信息重新创建适当的方法调用。



为了简化问题,我们使用Cecil测试套件中的以下方法:

  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参数)
instance.GenericArguments.Add(argument);

返回实例;
}

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(self.Parameters中的var参数)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));

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

返回参考;
}

通过这些,您可以将代码重写为:

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


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?

解决方案

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

这篇关于Mono.Cecil:从其他程序集中调用GENERIC基类的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆