c# - 如何使用 "Expression.Parameter"和 "Expression.Call"API 调用通用方法

标签 c# generics expression-trees

使用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/

相关文章:

c# - 需要帮助将 C# 转换为 VB

javascript - MVC - 我的表单没有传递我的选择下拉列表的选定选项

c# - 代码契约(Contract) : Ensures Unproven & Requires Unproven

java - 泛型和 ClassCastException

c# - 在 C# 中序列化和反序列化表达式树

c# - NSwag Wep Api 2 multipart/form-data 属性/文件上传

通用 curry 函数的 typescript 类型

c# - 将类型作为参数传递给属性

c# MemberExpression 对于已知类型,可以安全地重命名成员