【问题标题】:Dynamically create subclass which overrides virtual final methods动态创建覆盖虚拟最终方法的子类
【发布时间】:2014-10-13 08:20:34
【问题描述】:

我有类和接口:

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."

有什么想法可以做到吗?

PS:如果您愿意,我可以发布提到的代码。

编辑 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ł 科莫罗夫斯基: 谢谢你的答案。它有效,但只是部分有效。程序不再抛出错误,但在此示例中:

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

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

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

谢谢你们的帮助。不幸的是,我无法将两个答案都标记为正确答案,但两者对于解决问题都很重要。

【问题讨论】:

  • 欢迎来到 SO。我们需要看到“一些代码”——你到底在哪里得到了那个错误。
  • final?你说的是 C# 还是其他语言?
  • 我可能弄错了您在此处尝试执行的操作,但您不能覆盖非虚拟方法。 'new' 关键字提供了一种声明类成员的方法,其语法与基类成员完全相同,但这与覆盖它不同。因此,对于非虚拟(最终/密封)的方法,您必须生成非虚拟成员。话虽如此,让子类的非虚拟成员只调用他们的基本亲属有什么意义?

标签: c# class reflection methods overriding


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    问题在于您实际上是在尝试覆盖非虚拟方法。您有 2 个选项。第一个是使 TestClass.GetStatus 方法成为虚拟方法。当你这样做时,你将能够覆盖它。第二种解决方案是隐藏此方法。在我看来,如果您:

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

    【讨论】:

      猜你喜欢
      • 2012-04-02
      • 1970-01-01
      • 2012-06-19
      • 2013-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-02
      • 1970-01-01
      相关资源
      最近更新 更多