Dynamically create a subclass that overrides virtual target methods

I have a class and an interface:

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

public interface ITestInterface
{
    int GetStatus();
}

      

and I would like to dynamically subclass TestClass that looks like this:

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

      

I have code that can subclass and override all virtual methods, but when the method is virtual final (GetStatus), I get:

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

      

Any ideas how this can be done?

PS: I can post the code mentioned if you want.

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 thrown in var type = builder.CreateType();

EDIT 2: @Rahul: The language is C # and as you can see in the GetVirtualMethods method there is an IsVirtual property. The IsFinal property also exists and returns true for the GetStatus method.

EDIT 3: @ Wim.van.Gool: Yes you are right - I cannot override non-virtual methods. What I'm trying to do here is hide the base implementation of "GetStatus" with a dummy implementation that calls the base method. Why would this be helpful? Imagine that the GetSubClass method returns you a class that behaves like the base one, but for example added log methods before and after calling the base implementation.

@ Michał Komorowski: Thanks for the answer. It works, but only partially. The program no longer throws an error, but in this example:

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

      

The GetStatus method is called directly from the base (TestClass), not from a dynamically created subclass. I tried to add: builder.AddInterfaceImplementation(typeof(ITestInterface));

but it didn't make any difference.

EDIT 4: @danish: Thanks for the answer. He is working now. NewSlot

keeps the problem. In fact, all of the data attribute: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual

.

Thanks guys for the help. Unfortunately I cannot mark both answers as correct, but both were important in solving the problem.

+1


source to share


2 answers


Whenever a class implements an interface method, the CLR requires that the method be marked as virtual and final set to true. However, this method is not really virtual. To get the actual virtual methods you need to check IsVirtual

for true

and IsFinal

for false

.



In your case, you are actually looking for latent intentional behavior. Hence, a new method must be created and released for the subclass. From what I understand, the NewSlot

method attribute will shadow the base class method (can someone confirm? I'm not too sure about this).

0


source


The problem is that you are actually trying to override a method that is not virtual. You have 2 options. The first is to make the TestClass.GetStatus method virtual. When you do this, you can override it. The second solution is to hide this method. It seems to me that your code will work if you:



  • Remove next line builder .DefineMethodOverride (newMethodInfo, virtualMethodInfo);
  • In the DefineNewVirtualMethod, use MethodAttributes.HideBySig instead of MethodAttributes.Virtual.
0


source







All Articles