In my project I have the following two methods which are used to call other methods with variable number and types of arguments:
private static object InvokeMethod(MethodInfo method, MyTargetClass target, object[] initialArgs, object[] additionalArgs)
{
object result = null;
var methodParams = method.GetParameters();
if (methodParams.Length == 0)
{
result = method.Invoke(target, null);
}
else
{
var args = CollectArguments(method, initialArgs, paramArgs);
// collect arguments with default values for missing ones and invoke the method
result = method.Invoke(target,args);
}
return result;
}
private static object[] CollectArguments(MethodInfo method, object[] initialArgs, object[] paramArgs)
{
List<object> allArgs = new List<object>();
allArgs.AddRange(initialArgs);
// append all param style args
if (paramArgs != null)
allArgs.AddRange(paramArgs);
// do we have enough arguments?
int missing = method.GetParameters().Length - allArgs.Count;
if (missing < 0)
throw new InvalidOperationException(string.Format("Too many arguments passed to the method {0}", method.Name));
for (int i = 0; i < missing; i++)
{
// all parameters after mandatory should be with optional default values, so pass them
allArgs.Add(Type.Missing);
}
return allArgs.ToArray();
}
My InvokeMethod is called often, so I would like to optimize it. I already am caching MethodInfo references. All the methods to call have a custom attribute and I collect those methods in a dictionary upon application startup. I managed to cache lambda expressions for constructors of MyTargetClass derived classes and now I would like also to cache lambda expressions for method calls.
I have found some examples how to create lambda expressions for Expression.Call, but the problem is that I don't know how to deal with variable arguments, which include also default values (that's why I am adding Type.Missing) and also there might be ref arguments.
There are some additional rules for the methods I want to invoke (omitted for brevity), but basically methods might look like this:
MyMethod1(int a, long b = 2)
MyMethod2(ref long a, ref long b, string something)
I always know those special cases with ref arguments, so after method.Invoke I collect ref variables back:
a = (long)allArgs[0];
b = (long)allArgs[1];
where a and b are passed by ref from outside.
How do I achieve the same functionality using Expression.Call? How do I pass variable count of arguments to it and how do I get ref values?