C# 如何以更通用的方式将运算符应用于表达式

C# 如何以更通用的方式将运算符应用于表达式,c#,generics,lambda,expression,C#,Generics,Lambda,Expression,我处理的是动态查询,我不确定如何重构这个示例代码 如果我们想支持更多的操作员/属性,那么梯形图将是巨大的 var property = "AccountBalance"; var operator = ">"; var value = 5; var query = _context.Users; if (property == "AccountBalance") { if (operator == ">") { query = query.Where

我处理的是动态查询,我不确定如何重构这个示例代码 如果我们想支持更多的操作员/属性,那么梯形图将是巨大的

var property = "AccountBalance";
var operator = ">";
var value = 5;

var query = _context.Users;
if (property == "AccountBalance")
{
    if (operator == ">")
    {
        query = query.Where(x => x.AccountBalance > value);
    }
}
我希望将其重构为非常通用且可重用的内容,如:

// I simplified comparison here because afaik I'd have to use `CompareTo` due to IComparable   
// instead of >, but I don't think it is important right now

public Expression<Func<T, bool>> GetExpression<T, U>(string operator, U value, Func<T, object property) where U : IComparable, T : class
{
    switch (operator)
    {
        case ">":
            return property > value;    
        case "<":
            return property < value;
        (...)
        default:
            throw new Exception("undefined operator");
    }
}
//我在这里简化了比较,因为由于IComparable,我不得不使用'CompareTo'
//而不是>,但我认为现在这并不重要
公共表达式GetExpression(字符串运算符、U值、Func值;
案例“”,5,新函数(x=>x.AccountBalance));
query=query.Where(表达式);
但我不知道如何将属性传递给该函数


newfunc(x=>x.AccountBalance)
这是伪代码思想

您可以将
GetExpression
、a
Func
添加为方法的参数,其中
T
是要传递其属性之一的对象的类型,
U
是属性的类型。然后,当调用方法时,可以传递lambda

例如

var expression=GetExpression(“>”,5,x=>x.AccountBalance);
PS

您必须更改方法签名,如下所示:

public Expression<Func<T, bool>> GetExpression<T, U>
   (string operator, U value, Func<T, U> property) where U : IComparable, T : class
公共表达式GetExpression
(字符串运算符,U值,Func属性),其中U:IComparable,T:class
更新

您可以尝试以下方法:

public Expression<Func<T, bool>> GetExpression<T, U>
    (string op, U value, Func<T, U> property) 
        where  T: class 
        where U : IComparable
{
    switch (op)
    {
        case ">":
            return p => value.CompareTo(property(p)) > 0;
        case "<":
            return p => value.CompareTo(property(p)) < 0;
        default:
            throw new Exception("undefined operator");
    }
}
公共表达式GetExpression
(字符串op、U值、Func属性)
T:在哪里上课
其中U:i可比较
{
开关(op)
{
案例“>”:
返回p=>value.CompareTo(属性(p))>0;

case“您可以使用Linq表达式API动态生成表达式

public static Expression<Func<T, bool>> GetExpression<T, U>(string property,
    string @operator, U value) where U : IComparable
{
    var p = Expression.Parameter(typeof(T), "p");
    var member = Expression.PropertyOrField(p, property);
    var constant = Expression.Constant(value);
    var compareToArgument = Expression.Convert(constant, typeof(object));

    var method = typeof(IComparable).GetMethod("CompareTo");
    var call = Expression.Call(member, method, compareToArgument);

    Expression body = null;

    if (@operator == ">")
        body = Expression.GreaterThan(call, Expression.Constant(0));
    else if (@operator == "<")
        body = Expression.LessThan(call, Expression.Constant(0));

    // todo: throw if @operator is unknown

    var lambda = Expression.Lambda<Func<T, bool>>(body, p);
    return lambda;
}

几乎可以工作,但
运算符'>'不能应用于'Func'和'U'类型的操作数。
此处:
返回属性>值;
是否需要向
T:class
添加一些比较接口?
public Expression<Func<T, bool>> GetExpression<T, U>
    (string op, U value, Func<T, U> property) 
        where  T: class 
        where U : IComparable
{
    switch (op)
    {
        case ">":
            return p => value.CompareTo(property(p)) > 0;
        case "<":
            return p => value.CompareTo(property(p)) < 0;
        default:
            throw new Exception("undefined operator");
    }
}
public static Expression<Func<T, bool>> GetExpression<T, U>(string property,
    string @operator, U value) where U : IComparable
{
    var p = Expression.Parameter(typeof(T), "p");
    var member = Expression.PropertyOrField(p, property);
    var constant = Expression.Constant(value);
    var compareToArgument = Expression.Convert(constant, typeof(object));

    var method = typeof(IComparable).GetMethod("CompareTo");
    var call = Expression.Call(member, method, compareToArgument);

    Expression body = null;

    if (@operator == ">")
        body = Expression.GreaterThan(call, Expression.Constant(0));
    else if (@operator == "<")
        body = Expression.LessThan(call, Expression.Constant(0));

    // todo: throw if @operator is unknown

    var lambda = Expression.Lambda<Func<T, bool>>(body, p);
    return lambda;
}
var expression = GetExpression<User, int>("AccountBalance", ">", 5);
query = query.Where(expression);
var p = Expression.Parameter(typeof(T), "p");
var member = Expression.PropertyOrField(p, property);
var constant = Expression.Constant(value);

Expression body = null;

if (@operator == ">=")
    body = Expression.GreaterThanOrEqual(member, constant);
if (@operator == "<=")
    body = Expression.LessThanOrEqual(member, constant);