【问题标题】:How do I create a generic Expression that has an expression as a parameter如何创建将表达式作为参数的通用表达式
【发布时间】:2013-11-05 12:48:48
【问题描述】:

在 ASP.Net MVC 中有一个 DisplayNameFor(x=>x.Title) 助手。 我想实现类似的行为。

我希望有一个方法可以接受基于User 类(u=>u.Birthdate 或 u=>u.Name)的表达式、一个操作数(更大、更少、等于)和类似DateTime.Now 的值并返回表达式u=>u.Birthdate > DateTime.Now

我知道我必须手动从片段中构建结果表达式。我无法理解的是传递和处理属性表达式。

编辑:
我想调用像
GetFilterPredicate(u=>u.Birthdate,FilterOps.GreaterThan,DateTime.Parse("01.01.2013") 这样的方法 或
GetFilterPredicate(u=>u.SomeIntProperty,FilterOps.Equals,2)

更新:我创建了一个包含此问题的解决方案以及集合属性过滤的存储库 https://github.com/Alexander-Taran/Lambda-Magic-Filters

【问题讨论】:

  • 您要传递表达式树还是 lambda?
  • 在问题中添加了我想要的用例

标签: c# lambda expression-trees func


【解决方案1】:

这是否满足您的需求?

[TestClass]
public class UnitTest1
{
    public Expression<Predicate<T>> GetFilterPredicate<T, R>(Expression<Func<T, R>> selector, FilterOps operand, R value)
    {
        var parameter = selector.Parameters[0];

        var left = selector.Body;
        var right = Expression.Constant(value);

        var binaryExpression = Expression.MakeBinary(operand.ToExpressionType(), left, right);
        return Expression.Lambda<Predicate<T>>(binaryExpression, parameter);
    }

    [TestMethod]
    public void TestMethod1()
    {
        var p1 = this.GetFilterPredicate((User u) => u.Birthday.TimeOfDay.Hours, FilterOps.LessThan, 12);
        var p2 = this.GetFilterPredicate((User u) => u.Size, FilterOps.Equal, 180);

        var user = new User() { Birthday = new DateTime(2000, 1, 1), Size = 180 };

        Assert.IsTrue(p1.Compile()(user));
        Assert.IsTrue(p2.Compile()(user));
    }
}

public enum FilterOps
{
    GreaterThan, LessThan, Equal
}
public static class MyExtensions
{
    public static ExpressionType ToExpressionType(this FilterOps operand)
    {
        switch (operand)
        {
            case FilterOps.GreaterThan: return ExpressionType.GreaterThan;
            case FilterOps.LessThan: return ExpressionType.LessThan;
            case FilterOps.Equal: return ExpressionType.Equal;
            default: throw new NotSupportedException();
        }
    }
}

public class User { public DateTime Birthday { get; set; } public int Size { get; set; } }

【讨论】:

  • 我认为结果应该是一个Expression,而不是一个已编译的委托,但这是对您的代码的一个微不足道的修改。
  • 几乎正是我所需要的!!!非常感谢。只需将 Predicate 更改为 Func。带有测试套件的完整解决方案的奖励积分。
【解决方案2】:

我相信这就是你的目标。

public Func<User, bool> MyMethod<TProperty>(Expression<Func<User, TProperty>> func, ComparisonPredicate op, TProperty value)
{


}

public enum ComparisonPredicate
{
    Equal,
    Unequal,
    LessThan,
    LessThanOrEqualTo,
    GreaterThan,
    GreaterThanOrEqualTo
}

【讨论】:

  • 这是我的目标,是的。现在开始飙升。
  • 返回类型也应该是Expression
【解决方案3】:

类似这样的:

Expression<Func<TObject, TProperty>> GenerateAssignExpression<TObject, TProperty>
    (Expression<Func<TObject, TProperty>> getExpression,
    TProperty Value)
{
    var getExpressionBody = getExpression.Body as MemberExpression;
    if (getExpressionBody == null)
    {
        throw new Exception("getExpressionBody is not MemberExpression: " + 
                getExpression.Body);
    }

    var objectParameter = (ParameterExpression)getExpression.Parameters[0];
    ConstantExpression constant = Expression.Constant(Value, typeof(TProperty));
    var expAssign = Expression.Assign(e.Body, constant);

    return Expression.Lambda<Func<TObject, TProperty>>(expAssign, 
            objectParameter, 
            valueParameter);
}

【讨论】:

  • 为什么是Assign?该问题涉及GreaterThanEquals等操作。
  • @svick,这段代码来自我的小实验,我认为更改为所需的表达式不是一个大问题,主要是OP询问如何将表达式作为参数传递。
【解决方案4】:

以下方法将生成一个二进制表达式,该表达式将具有布尔返回值

Expression<Predicate<TObject>> 
     GenerateAssignExpression<TObject, TProperty>(
         Expression<Func<TObject, TProperty>> expression,
         ExpressionType op, 
         TProperty Value)
    {   
        return Expression.Lambda<Predicate<TObject>>(
            Expression.MakeBinary(op, expression, Expression.Constant(Value)),
            getExpression.Parameters[0]);
    }

如果您希望使用通用返回类型,只需将 Predicate&lt;TObject&gt; 更改为 Func&lt;TObject,TReturn&gt;(您需要替换两次出现的 `Predicate´

【讨论】:

  • 你不能像这样创建一个新的Parameter,你需要重用原来的getExpression
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-09
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多