It's possible to just invoke the methodInfo directly, or somewhat more complicatedly use the Linq Expressions library to create a Func<object,object,...> that can be invoked that does all of the required casts.
Note that I am assuming that you do know the number of parameters at compile time, as your question indicates with the Func<,,,> usage.
It's a bit involved, but basically comes down to using Expression.Lambda(...).Compile() to create the final func that will be invoked, Expression.Convert() to cast from objects to the actual types so that Expression.Call() can be used to call the method on objects with the correct types from our object Expression.Parameters
This example shows how to implement both approaches.
using System;
using System.Linq.Expressions;
namespace SO
{
class SO69847110
{
public TR fn<T1,T2,T3,TR>(T1 p1, T2 p2, T3 p3)
{
if(typeof(TR) == typeof(string))
{
return (TR)(object)"Hello World";
}
return default(TR);
}
static void Main(string[] args)
{
var returnType = typeof(string);
var paramType = typeof(int);
var delegMethodType = typeof(Func<,,,>).
MakeGenericType(
paramType,
paramType,
paramType,
returnType
);
var methodInfo = typeof(SO69847110).GetMethod("fn")
.MakeGenericMethod(
paramType,
paramType,
paramType,
returnType
);
var obj = new SO69847110();
//var deleg = Delegate.CreateDelegate(delegMethodType, obj, methodInfo);
var methodInfoInvoke = methodInfo.Invoke(obj, new object[] { 0, 1, 2 });
Console.WriteLine(methodInfoInvoke);
var param1 = Expression.Parameter(typeof(object));
var param2 = Expression.Parameter(typeof(object));
var param3 = Expression.Parameter(typeof(object));
var convertedParameterExpressions =
new Expression[]
{
Expression.Convert(param1,paramType),
Expression.Convert(param2,paramType),
Expression.Convert(param3,paramType),
};
var expressed = Expression.Lambda<Func<object, object, object, object>>(
Expression.Call(
Expression.Convert(Expression.Constant(obj), obj.GetType()),
methodInfo,
convertedParameterExpressions
),
tailCall:false,
parameters: new[]
{
param1,
param2,
param3
}
).Compile();
var expressedInvoked = expressed.Invoke(obj, 0, 1, 2);
Console.WriteLine(expressedInvoked);
}
}
}
If you want to call the same function for multiple objects, then you can also make the subject a Func parameter too - just make it a separate Expression.Parameter(typeof(object)) at the start, include that parameter instead of the Expression.Constant and also include in the list of parameters for Expression.Lambda. Then also pass in the subject as a parameter when invoking.
Expressionor a dynamic methodInvokeon a cacheddelegMethodType.GetMethod("Invoke")is actually faster, see dotnetfiddle.net/VgJpqo. Moral of the story: race your horses