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
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.
Hey Ivan,
My mistake. The ‘r’ in ‘ReturnType’ should be lower case.
Thanks for catching that. I updated the code in this post
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