动态创建覆盖虚拟最终方法的子类
本文关键字:方法 子类 创建 覆盖 虚拟 动态 | 更新日期: 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方法成为虚拟的。当你这样做时,你将能够覆盖它。第二个解决方案是隐藏这个方法。在我看来,如果你:,你的代码就会工作
- 删除以下行builder.DefineMethodOverride(newMethodInfo,virtualMethodInfo)
- 在DefineNewVirtualMethod方法中,使用MethodAttributes.HideBySig而不是MethodAttributes.Virtual