动态创建覆盖虚拟final方法的子类 [英] Dynamically create subclass which overrides virtual final methods

查看:288
本文介绍了动态创建覆盖虚拟final方法的子类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有类和接口:

  public class TestClass:ITestInterface 
{
public int GetStatus ()
{
return -1;
}
}

public interface ITestInterface
{
int GetStatus();
}

,我想动态创建TestClass的子类, / p>

  public class TestClass2:TestClass 
{
public new int GetStatus()
{
return base.GetStatus();
}
}



我有一些代码可以创建子类和覆盖所有虚拟方法但是当方法是virtual final(GetStatus)我得到:

 在方法实现中引用的声明不能是最终方法。任何想法如何做?


编辑1:



'一些代码':

  public static T GetSubClass< T>()其中T:class 
{
var builder = DefineType< T>();
DefineOverrideMethods(builder,typeof(T));
var type = CreateType(builder);
return(T)Activator.CreateInstance(type);
}

private static TypeBuilder DefineType< T>()其中T:class
{
return _moduleBuilder.DefineType(Proxy_+ typeof ,
TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.Public,typeof(T));
}

private static void DefineOverrideMethods(TypeBuilder builder,Type type)
{
foreach(var virtualMethodInfo in GetVirtualMethods(type))
{
var parameters = GetMethodParametersTypes(virtualMethodInfo);
var newMethodInfo = DefineNewVirtualMethod(builder,virtualMethodInfo,parameters);

var il = newMethodInfo.GetILGenerator();
var local = EmitCreateLocal(il,newMethodInfo);

EmitCallBaseMethod(il,virtualMethodInfo);
EmitSaveReturnToLocal(il,local);
EmitReturnMethod(il,virtualMethodInfo,local);

builder.DefineMethodOverride(newMethodInfo,virtualMethodInfo);
}
}

private static IEnumerable< MethodInfo> GetVirtualMethods(Type type)
{
return type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(q => q.IsVirtual);
}

private static Type [] GetMethodParametersTypes(MethodInfo virtualMethodInfo)
{
return virtualMethodInfo.GetParameters()。Select(q => q.ParameterType)。 ToArray();
}

private static MethodBuilder DefineNewVirtualMethod(TypeBuilder builder,MethodInfo virtualMethodInfo,Type [] parameters)
{
return builder.DefineMethod(virtualMethodInfo.Name,
MethodAttributes.Public | MethodAttributes.Virtual,
virtualMethodInfo.ReturnType,parameters);
}

private static void EmitSaveReturnToLocal(ILGenerator il,LocalBuilder local)
{
il.Emit(OpCodes.Stloc_S,local);
il.Emit(OpCodes.Ldloc_S,local);
}

private static LocalBuilder EmitCreateLocal(ILGenerator il,MethodBuilder newMethodInfo)
{
return il.DeclareLocal(newMethodInfo.ReturnType);
}

private static类型CreateType(TypeBuilder builder)
{
builder.DefineDefaultConstructor(MethodAttributes.Public);
var type = builder.CreateType();
return type;
}

private static void EmitReturnMethod(ILGenerator il,MethodInfo methodInfo,LocalBuilder local)
{
il.Emit(OpCodes.Ldloc_S,local);
il.Emit(OpCodes.Ret);
}

private static void EmitCallBaseMethod(ILGenerator il,MethodInfo virtualMethodInfo)
{
ushort index = 0;
while(index< virtualMethodInfo.GetParameters()。Length + 1)
il.Emit(OpCodes.Ldarg,index ++);

il.Emit(OpCodes.Call,virtualMethodInfo);
}

异常抛出 var type = builder.CreateType ();



EDIT 2:
@Rahul:
语言是C#正如你可以在方法GetVirtualMethods中看到的,有属性IsVirtual。
属性'IsFinal'也存在,并为'GetStatus'方法返回true。



编辑3:
@ Wim.van.Gool:
是的,虚拟方法。我想在这里做的是隐藏'GetStatus'的基本实现与虚拟实现调用基本方法。
为什么它会有用?想象一下,方法GetSubClass返回一个类,它的行为像base,但例如在基本实现调用之前和之后添加了日志方法。



@MichałKomorowski:
谢谢回答。它工作,但只有部分。程序不会抛出错误,但在此示例中:

  ITestInterface obj = StaticClass.GetSubClass< TestClass>(); 
obj.GetStatus();

'GetStatus'方法直接从base(TestClass)调用而不是从动态创建的子类。
我试图添加: builder.AddInterfaceImplementation(typeof(ITestInterface)); 但它没有任何区别。



编辑4:
@danish:谢谢回答。它现在工作。 NewSlot 保存问题。
实际上整个属性数据: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual



感谢大家的帮助。不幸的是,我不能将两个答案标记为正确的答案,但两者都是解决问题的重要。

解决方案

当一个类实现了一个接口方法时,CLR需要将该方法标记为virtual,并将final设置为true。然而在现实中,这种方法不是真的虚拟。要获得实际的虚方法,你需要检查 IsVirtual for true IsFinal false



在你的情况下,你实际上是在寻找隐藏预期的行为。因此,需要为子类创建和发出一个新的方法。从我的理解, NewSlot 方法属性将影子基类方法(有人可以确认?我不太确定这一个)。


I have class and interface:

public class TestClass : ITestInterface
{
    public int GetStatus()
    {
        return -1;
    }
}

public interface ITestInterface
{
    int GetStatus();
}

and I would like to dynamically create subclass of TestClass which will look like:

public class TestClass2 : TestClass
{
    public new int GetStatus()
    {
        return base.GetStatus();
    }
}

I have some code which can create subclasses and overrides all virtual methods but when method is virtual final (GetStatus) I'm getting:

"Declaration referenced in a method implementation cannot be a final method."

Any ideas how it can be done?

PS: I can post code mentioned if you'd like.

EDIT 1:

'Some code':

    public static T GetSubClass<T>() where T : class
    {
        var builder = DefineType<T>();
        DefineOverrideMethods(builder, typeof(T));
        var type = CreateType(builder);            
        return (T)Activator.CreateInstance(type);            
    }

    private static TypeBuilder DefineType<T>() where T : class
    {
        return _moduleBuilder.DefineType("Proxy_" + typeof (T).Name,
            TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.Public, typeof (T));
    }

    private static void DefineOverrideMethods(TypeBuilder builder, Type type)
    {
        foreach (var virtualMethodInfo in GetVirtualMethods(type))
        {
            var parameters = GetMethodParametersTypes(virtualMethodInfo);
            var newMethodInfo = DefineNewVirtualMethod(builder, virtualMethodInfo, parameters);

            var il = newMethodInfo.GetILGenerator();
            var local = EmitCreateLocal(il, newMethodInfo);

            EmitCallBaseMethod(il, virtualMethodInfo);
            EmitSaveReturnToLocal(il, local);
            EmitReturnMethod(il, virtualMethodInfo, local);

            builder.DefineMethodOverride(newMethodInfo, virtualMethodInfo);
        }
    }

    private static IEnumerable<MethodInfo> GetVirtualMethods(Type type)
    {
        return type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(q => q.IsVirtual);
    }

    private static Type[] GetMethodParametersTypes(MethodInfo virtualMethodInfo)
    {
        return virtualMethodInfo.GetParameters().Select(q => q.ParameterType).ToArray();
    }   

    private static MethodBuilder DefineNewVirtualMethod(TypeBuilder builder, MethodInfo virtualMethodInfo, Type[] parameters)
    {
        return builder.DefineMethod(virtualMethodInfo.Name,
            MethodAttributes.Public | MethodAttributes.Virtual,
            virtualMethodInfo.ReturnType, parameters);
    }

    private static void EmitSaveReturnToLocal(ILGenerator il, LocalBuilder local)
    {
        il.Emit(OpCodes.Stloc_S, local);
        il.Emit(OpCodes.Ldloc_S, local);
    }

    private static LocalBuilder EmitCreateLocal(ILGenerator il, MethodBuilder newMethodInfo)
    {
        return il.DeclareLocal(newMethodInfo.ReturnType);
    }

    private static Type CreateType(TypeBuilder builder)
    {
        builder.DefineDefaultConstructor(MethodAttributes.Public);
        var type = builder.CreateType();
        return type;
    }

    private static void EmitReturnMethod(ILGenerator il, MethodInfo methodInfo, LocalBuilder local)
    {
        il.Emit(OpCodes.Ldloc_S, local);
        il.Emit(OpCodes.Ret);
    }

    private static void EmitCallBaseMethod(ILGenerator il, MethodInfo virtualMethodInfo)
    {
        ushort index = 0;
        while (index < virtualMethodInfo.GetParameters().Length + 1)
            il.Emit(OpCodes.Ldarg, index++);

        il.Emit(OpCodes.Call, virtualMethodInfo);
    }           

Exception is throwed at var type = builder.CreateType();

EDIT 2: @Rahul: Language is C# and as you can see in method 'GetVirtualMethods' there is property 'IsVirtual'. Property 'IsFinal' exists there too and returns true for 'GetStatus' method.

EDIT 3: @Wim.van.Gool: Yes you are right - I can not override non virtual methods. What I am trying to do here is to hide base implementation of 'GetStatus' with dummy implementation which calls base method. Why it would be useful? Imagine that method 'GetSubClass' returns you a class that behaves like base but for example have added log methods before and after base implementation calls.

@Michał Komorowski: Thank you for answer. It works, but only partially. Program do not throws error anymore but in this example:

ITestInterface obj = StaticClass.GetSubClass<TestClass>();
obj.GetStatus();  

'GetStatus' method is called directly from base (TestClass) not from dynamically created subclass. I tried to add: builder.AddInterfaceImplementation(typeof(ITestInterface)); but it did not make any difference.

EDIT 4: @danish: Thank you for answer. It is now working. NewSlot saves problem. Actually whole attribute data: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual.

Thank you guys for helping. Unfortunately I can not mark both answers as a correct ones but both were significant for resolving problem.

解决方案

Whenever a class implements an interface method, CLR needs that method to be marked as virtual and also final is set to true. However in reality this method is not really virtual. To get actual virtual methods, you need to check IsVirtual for true and IsFinal for false.

In your case, you are actually looking for hiding the intended behaviour. Hence a new method needs to be created and emitted for sub class. From what I understand, NewSlot method attribute will shadow the base class method(can someone confirm? I am not too sure about this one).

这篇关于动态创建覆盖虚拟final方法的子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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