使用Expression Tree API我想生成如下所示的代码:
FieldInfo enumFieldInfo = enumFieldInfoSet.SingleOrDefault(fieldInfo => fieldInfo.Name == enumName);
我已经编写了这段代码,但它不起作用:
var enumerableType = typeof(Enumerable);
var enumerableGenericType = typeof(IEnumerable<>).MakeGenericType(typeof(FieldInfo));
var predicateGenericType = typeof(Func<>).MakeGenericType(typeof(Func<FieldInfo, bool>));
ParameterExpression fieldInfoSource = Expression.Parameter(enumerableGenericType, "fieldInfoSource");
ParameterExpression predicateSource = Expression.Parameter(predicateGenericType, "funcPredicateOnFieldInfo");
var arrayOfTypes = new Type[] { enumerableGenericType, predicateGenericType };
MethodCallExpression SingleOrDefaultMethodCall = Expression.Call(enumerableType, "SingleOrDefault",arrayOfTypes, fieldInfoSource, predicateSource);
这是运行时错误:“System.Linq.Enumerable”类型上的泛型方法“SingleOrDefault”与提供的类型参数和参数不兼容。如果方法是非泛型的,则不应提供类型参数。
我尝试了多种类型强制的组合,但仍然没有找到正确的组合。我知道 SingleOrDefault 是 Enumerable 类的扩展方法,它需要两个参数;我通过调试器查看了代码,并编写了在运行时检查其属性的代码;我缺少什么。
最佳答案
问题是您正在使用采用类型的 Expression.Call
重载,而对于静态方法,您需要使用 MethodInfo
进行重载。
void Main()
{
Expression<Func<IEnumerable<FieldInfo>, Func<FieldInfo,bool>, FieldInfo>> singleOrDefaultExpr = (l,p) => l.SingleOrDefault(p);
var callSource = (MethodCallExpression)singleOrDefaultExpr.Body;
var method = callSource.Method;
var collectionParameter = Expression.Parameter(typeof(IEnumerable<FieldInfo>), "enumFieldInfoSet");
var enumNamePredicateParameter = Expression.Parameter(typeof(Func<FieldInfo,bool>), "enumNamePredicate");
var body = Expression.Call(method, collectionParameter, enumNamePredicateParameter);
var lambda = Expression.Lambda<Func<IEnumerable<FieldInfo>, Func<FieldInfo, bool>, FieldInfo>>(body, collectionParameter, enumNamePredicateParameter);
var f = lambda.Compile();
Console.WriteLine(f(typeof(Apple).GetFields(), fi => fi.Name == "color").Name);
}
class Apple
{
public string color;
}
此外,您还可以使用另一种方法来查找所需的 MethodInfo:
var method = typeof(Enumerable)
.GetMethods()
.Single(m => m.Name == "SingleOrDefault" && m.GetParameters().Count() == 2)
.MakeGenericMethod(new[] {typeof(FieldInfo)});
更新:
实际上有一个更简单的方法,并且您的方法是正确的,但是您的代码有错误。
var collectionParameter = Expression.Parameter(typeof(IEnumerable<FieldInfo>), "enumFieldInfoSet");
var enumNamePredicateParameter = Expression.Parameter(typeof(Func<FieldInfo,bool>), "enumNamePredicate");
var body = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { typeof(FieldInfo)}, collectionParameter, enumNamePredicateParameter);
var lambda = Expression.Lambda<Func<IEnumerable<FieldInfo>, Func<FieldInfo, bool>, FieldInfo>>(body, collectionParameter, enumNamePredicateParameter);
问题是 SingleOrDefault 只有一个通用类型参数:本例中为“FieldInfo”:
SingleOrDefault<FieldInfo>(....
不要将其与方法参数混淆,其中有两个:
SingleOrDefault<GenericParameter>(
this IEnumerable<GenericParameter> firstMethodParameter,
Func<GenericParameter, bool> secondMethodParameter
)
关于c# - 如何使用 "Expression.Parameter"和 "Expression.Call"API 调用通用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33663439/