c# - Expression.MethodCallExpression 将 MemberExpression 作为参数传递

标签 c# lambda linq-expressions

我正在尝试创建一个带有方法调用的通用表达式

Enumerable.Contains

所以基本上我想实现这个简单的 lambda

x => collection.Contains(x.SomeProperty)

到目前为止我的代码如下所示:

ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);

MethodCallExpression methodCall = Expression.Call(
    typeof(Enumerable),
    "Contains",
    new Type[] { typeof(Object) },
    Expression.Constant(new Object[] { 1, 2, 3 }),
    memberExpression
);

但随后它就转储了

InvalidOperationException: No generic method 'Contains' on type 'System.Linq.Enumerable' is compatible with the supplied type
arguments and arguments. No type arguments should be provided if the
method is non-generic

如果我只是传递 paramterExpression 它工作正常,但这不是我想要的。

我的问题是现在。有没有办法将 MemberExpression 传递给 Contains 方法调用?

最佳答案

这取决于TypememberExpression这又取决于所访问的属性的类型。

例如以下作品:

void Main()
{
    string propertyName = "ObjectProperty";
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
    MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
    MethodCallExpression methodCall = Expression.Call(
        typeof(Enumerable),
        "Contains",
        new Type[] { typeof(object) },
        Expression.Constant(new Object[] { 1, 2, 3 }),
        memberExpression
    );

    Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
    public object ObjectProperty => 2;
    public int IntProperty => 4;
}

以下情况则不然:

void Main()
{
    string propertyName = "IntProperty";
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
    MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
    MethodCallExpression methodCall = Expression.Call(
        typeof(Enumerable),
        "Contains",
        new Type[] { typeof(object) },
        Expression.Constant(new Object[] { 1, 2, 3 }),
        memberExpression
    );

    Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
    public object ObjectProperty => 2;
    public int IntProperty => 4;
}

您可以使用new Type[] { memberExpression.Type }而不是new Type[] { typeof(object) }让代码适应属性的类型,尽管您还需要参数表达式的类型(在本例中为 Constant(new Object[] {...} ))匹配。

请注意,只有当它们是从相关类型 ( object ) 派生的引用类型时,您才能在此处执行隐式转换,因此返回 string 的属性或Uri就可以了(尽管在检查它是否包含在 1, 2, 3 数组中时显然总是 false ),但是返回 int 的属性不是因为它是拳击转换,而不是引用向上转换。

关于c# - Expression.MethodCallExpression 将 MemberExpression 作为参数传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53379981/

相关文章:

c# - Linq 查询中的 If 语句

Haskell:使用 RankNTypes 折叠记录构造函数

c# - 自定义 LINQ 风格的实现

c# - 在 Linq 中动态选择列和聚合函数

c# - 使用 NAudio 在特定 channel 播放声音

c# - 带有可选文本限定符的批量插入

c# - 如何在 C# 中将数据源绑定(bind)到 .rdlc 报告

c# - 将此 LINQ 表达式转换为 Lambda

amazon-web-services - 使用 Lambda 的自定义触发器更新 AWS CloudFormation

c# - 标签目标接收值意味着什么?