I came across this problem when I was writing a proxy library. I wanted to invoke the method of the base class but was instead invoking the overriden version. I was able to find the answer on Stack Overflow. Here is the code sample of the problem.
class BaseClass { public virtual void Test() { Console.WriteLine("Test() from BaseClass"); } } class OverridingClass : BaseClass { public override void Test() { Console.WriteLine("Test() from OverridingClass"); } } public static void Main() { var d = new OverridingClass(); typeof(BaseClass).GetMethod("Test").Invoke(d, null); }
The output is Test() from OverridingClass
but what I wanted is to call the version of Test()
defined in BaseClass
.
The answer on Stack Overflow was great. Basically he created a DynamicMethod
that would do the job. All I did was take that and generalize it so that it could be used as a drop in replacement for Invoke. So I created the following extension method:
public static object InvokeNotOverride(this MethodInfo methodInfo, object targetObject, params object[] arguments) { var parameters = methodInfo.GetParameters(); if (parameters.Length == 0) { if (arguments != null && arguments.Length != 0) throw new Exception("Arguments cont doesn't match"); } else { if (parameters.Length != arguments.Length) throw new Exception("Arguments cont doesn't match"); } Type returnType = null; if (methodInfo.ReturnType != typeof(void)) { returnType = methodInfo.ReturnType; } var type = targetObject.GetType(); var dynamicMethod = new DynamicMethod("", returnType, new Type[] { type, typeof(Object) }, type); var iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); // this for (var i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; iLGenerator.Emit(OpCodes.Ldarg_1); // load array argument // get element at index iLGenerator.Emit(OpCodes.Ldc_I4_S, i); // specify index iLGenerator.Emit(OpCodes.Ldelem_Ref); // get element var parameterType = parameter.ParameterType; if (parameterType.IsPrimitive) { iLGenerator.Emit(OpCodes.Unbox_Any, parameterType); } else if (parameterType == typeof(object)) { // do nothing } else { iLGenerator.Emit(OpCodes.Castclass, parameterType); } } iLGenerator.Emit(OpCodes.Call, methodInfo); iLGenerator.Emit(OpCodes.Ret); return dynamicMethod.Invoke(null, new object[] { targetObject, arguments }); }
With this extension method, the only change you have to make to the original code is to change the call to Invoke()
to InvokeNotOverride()
. Like so:
typeof(BaseClass).GetMethod("Test").Invoke(d, null);
becomes:
typeof(BaseClass).GetMethod("Test").InvokeNotOverride(d, null);
This has worked for my purposes, the method could benefit from caching since I think generating a DyanmicMethod each time is probably going to eat up memory.
You can download the source code from here: MethodInfoExtensions.zip
Ivan Mostyaev says
Placing this to Visual Studio project I got compilation error in this line:
var dynamicMethod = new DynamicMethod(“”, ReturnType, new Type[] { type, typeof(Object) }, type);
Visual Studio couldn’t locate ReturnType variable. I suppose there should be methodInfo.ReturnType instead of ReturnType.
Luis Perez says
Hey Ivan,
My mistake. The ‘r’ in ‘ReturnType’ should be lower case.
Thanks for catching that. I updated the code in this post
Hristo says
Hi Luis,
Thank you for the really nice and useful article. Please note, however, that calling the extension method and passing a struct as an argument will fail because of the parameterType.IsPrimitive check. You can change it to parameterType.IsValueType. I even think that actually we can remove the if statements and directly use the OpCodes.Unbox_Any field. For reference types, it will eventually use the CastClass field: https://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.unbox_any(v=vs.110).aspx
Hristo