寻找一种更优雅的方式通过表达式将 MethodInfo 转换为 Func
Searching for a more elegant way to turn a MethodInfo to a Func through Expressions
我有以下代码:
public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
{
var types = methodInfo.GetParameters().Select(p => p.ParameterType);
if (methodInfo.ReturnType.Equals((typeof(void))))
{
Debug.LogError("Cannot create a Func from a method without a return type!");
return null;
}
if (!methodInfo.IsStatic)
{
Debug.LogError("Cannot create a static Func from a non-static function!");
return null;
}
var funcParams = methodInfo.GetParameters();
var input = Expression.Parameter(typeof(object[]), "input");
Func<object[], object> returned = null;
//TODO research how to make this cleaner.
switch (funcParams.Length)
{
case 0:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo), input).Compile();
break;
case 1:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType)), input).Compile();
break;
case 2:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType)), input).Compile();
break;
case 3:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType)), input).Compile();
break;
case 4:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType)), input).Compile();
break;
case 5:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType)), input).Compile();
break;
case 6:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(5)), funcParams[5].ParameterType)), input).Compile();
break;
}
return returned;
}
}
这会将 MethodInfo 变成 Func/delegate,以便可以缓存它以提高性能,因为 MethodInfo.Invoke 性能不是很好。
代码似乎可以工作,但我无法编写更多 elegant/concise 方法来执行此操作,因为我提前不知道参数的数量。
有人可以帮忙吗?有可能吗?
我想你可以只使用 Expression.Lambda
的方法重载来完成它,即使你的方法没有参数。
像这样:
public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
{
var types = methodInfo.GetParameters().Select(p => p.ParameterType);
if (methodInfo.ReturnType.Equals((typeof(void))))
{
Console.WriteLine("Cannot create a Func from a method without a return type!");
return null;
}
if (!methodInfo.IsStatic)
{
Console.WriteLine("Cannot create a static Func from a non-static function!");
return null;
}
var input = Expression.Parameter(typeof(object[]), "input");
Func<object[], object> returned = GetParamatersExpressions(methodInfo, input);
return returned;
}
private static Func<object[], object> GetParamatersExpressions(MethodInfo methodInfo, ParameterExpression input)
{
Func<object[], object> result;
var funcParams = methodInfo.GetParameters();
IList<UnaryExpression> parms = new List<UnaryExpression>();
for (int i = 0; i < funcParams.Length; i++)
parms.Add(Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(i)), funcParams[i].ParameterType));
result = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo, parms), input).Compile();
return result;
}
希望对您有所帮助
我有以下代码:
public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
{
var types = methodInfo.GetParameters().Select(p => p.ParameterType);
if (methodInfo.ReturnType.Equals((typeof(void))))
{
Debug.LogError("Cannot create a Func from a method without a return type!");
return null;
}
if (!methodInfo.IsStatic)
{
Debug.LogError("Cannot create a static Func from a non-static function!");
return null;
}
var funcParams = methodInfo.GetParameters();
var input = Expression.Parameter(typeof(object[]), "input");
Func<object[], object> returned = null;
//TODO research how to make this cleaner.
switch (funcParams.Length)
{
case 0:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo), input).Compile();
break;
case 1:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType)), input).Compile();
break;
case 2:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType)), input).Compile();
break;
case 3:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType)), input).Compile();
break;
case 4:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType)), input).Compile();
break;
case 5:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType)), input).Compile();
break;
case 6:
returned = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo,
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(0)), funcParams[0].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(1)), funcParams[1].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(2)), funcParams[2].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(3)), funcParams[3].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(4)), funcParams[4].ParameterType),
Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(5)), funcParams[5].ParameterType)), input).Compile();
break;
}
return returned;
}
}
这会将 MethodInfo 变成 Func/delegate,以便可以缓存它以提高性能,因为 MethodInfo.Invoke 性能不是很好。
代码似乎可以工作,但我无法编写更多 elegant/concise 方法来执行此操作,因为我提前不知道参数的数量。
有人可以帮忙吗?有可能吗?
我想你可以只使用 Expression.Lambda
的方法重载来完成它,即使你的方法没有参数。
像这样:
public static Func<object[], object> CreateStaticFunc(this MethodInfo methodInfo)
{
var types = methodInfo.GetParameters().Select(p => p.ParameterType);
if (methodInfo.ReturnType.Equals((typeof(void))))
{
Console.WriteLine("Cannot create a Func from a method without a return type!");
return null;
}
if (!methodInfo.IsStatic)
{
Console.WriteLine("Cannot create a static Func from a non-static function!");
return null;
}
var input = Expression.Parameter(typeof(object[]), "input");
Func<object[], object> returned = GetParamatersExpressions(methodInfo, input);
return returned;
}
private static Func<object[], object> GetParamatersExpressions(MethodInfo methodInfo, ParameterExpression input)
{
Func<object[], object> result;
var funcParams = methodInfo.GetParameters();
IList<UnaryExpression> parms = new List<UnaryExpression>();
for (int i = 0; i < funcParams.Length; i++)
parms.Add(Expression.Convert(Expression.ArrayAccess(input, Expression.Constant(i)), funcParams[i].ParameterType));
result = Expression.Lambda<Func<object[], object>>(Expression.Call(methodInfo, parms), input).Compile();
return result;
}
希望对您有所帮助