Mono.Cecil: вызов метода GENERIC base class из другой сборки - PullRequest
6 голосов
/ 11 февраля 2011

Я продолжаю свой предыдущий вопрос: Mono.Cecil: вызов метода базового класса из другой сборки .
Я делаю то же самое, но если мой базовый класс универсальныйэто не работает

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

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

Он переплетает следующий код:

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

вместо

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

Что можно изменить?

1 Ответ

20 голосов
/ 15 февраля 2011

В своем предыдущем посте вы указали, что используете код вроде:

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

С их помощью вы можете переписать свой код как:

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