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.