动态创建覆盖虚拟最终方法的子类

本文关键字:方法 子类 创建 覆盖 虚拟 动态 | 更新日期: 2023-09-27 18:26:27

我有类和接口:

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

我想动态创建TestClass的子类,它看起来像:

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

我有一些代码可以创建子类并覆盖所有虚拟方法,但当方法是虚拟最终方法(GetStatus)时,我得到的是:

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

有什么想法吗?

附言:如果你愿意,我可以张贴上面提到的代码。

编辑1:

"某些代码":

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

var type = builder.CreateType(); 引发异常

第2版:@拉胡尔:语言是C#,您可以在方法"GetVirtualMethods"中看到属性"IsVirtual"。"那里也存在属性"IsFinal",并为"GetStatus"方法返回true。"。

第3版:@Wim.van.Gool:是的,你是对的-我不能覆盖非虚拟方法。我在这里要做的是用调用基方法的伪实现来隐藏"GetStatus"的基实现。为什么它会有用?假设方法"GetSubClass"为您返回一个行为类似于基类的类,但例如在基类实现调用之前和之后添加了日志方法。

@MichałKomorowski:谢谢你的回答。它有效,但只是部分有效。程序不再抛出错误,但在这个例子中:

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

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

第4版:@达尼什:谢谢你的回答。它现在正在工作。NewSlot节省了问题。实际上整个属性数据:MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual

谢谢你们的帮助。不幸的是,我不能把两个答案都标记为正确的,但这两个答案对解决问题都很重要。

动态创建覆盖虚拟最终方法的子类

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

在你的情况下,你实际上是在寻找隐藏预期行为的方法。因此,需要为子类创建并发出一个新方法。据我所知,NewSlot方法属性将覆盖基类方法(有人能确认吗?我对此不太确定)。

问题是您实际上试图覆盖一个非虚拟的方法。您有两个选项。第一个是使TestClass.GetStatus方法成为虚拟的。当你这样做时,你将能够覆盖它。第二个解决方案是隐藏这个方法。在我看来,如果你:,你的代码就会工作

  1. 删除以下行builder.DefineMethodOverride(newMethodInfo,virtualMethodInfo)
  2. DefineNewVirtualMethod方法中,使用MethodAttributes.HideBySig而不是MethodAttributes.Virtual