4

这是我的接口和枚举,稍微简化了一点。:

public interface IExpression
{
    ExpressionType ExpressionType { get; }
}

public interface ILiteralExpression : IExpression
{
    object Value { get; set; }
}

public interface IOperatorExpression : IExpression
{
    IExpression[] Operands { get; set; }

    string OperatorUniqueName { get; set; }

    IOperatorExpression SetOperand(int index, IExpression expression);
}

public enum ExpressionType
{
    Operator,
    Literal
}

要创建一个表达式,我可以这样做:

var expression = ExpressionManager.Engines["Default"].Parser.Parse("1 + 3 * 4 + \"myVariable\"");

这相当于这样的事情:

var expression1 = ExpressionManager.CreateOperator("Add", 2).
    SetOperand(0, ExpressionManager.CreateOperator("Add", 2).
        SetOperand(0, ExpressionManager.CreateLiteral(1)).
        SetOperand(1, ExpressionManager.CreateOperator("Multiply", 2).
            SetOperand(0, ExpressionManager.CreateLiteral(3)).
            SetOperand(1, ExpressionManager.CreateLiteral(4)))).
    SetOperand(1, ExpressionManager.CreateLiteral("myVariable"));

我希望能够(有效地)做这样的事情:

(from e in expression
 where e is ILiteralExpression && "myVariable".Equals(((ILiteralExpression)e).Value)
 select (ILiteralExpression)e).ToList().
 ForEach(e => e.Value = 2);

我想我需要做一些实施IQueryable或其他事情,但我不知道从哪里开始。有什么建议么?

4

2 回答 2

3

System.Linq.Expressions遍历表达式树,并使用类的工厂方法将每个元素从命名空间转换为对象Expression

如果你不能修改你的IExpression类来添加让你实现访问者模式的方法,你可以依靠类型检查旧样式:

private static Expression ConvertExpression(IExpression expr) {
    if (expr is ILiteralExpression) {
        return Expression.Constant(((ILiteralExpression)expr).Value);
    }
    if (expr is IOperatorExpression) {
        var ops = ((IOperatorExpression)expr)
            .Operands
            .Select(ConvertExpression)
            .ToList();
        var res = ops[0];
        for (int i = 1 ; i != ops.Length ; i++) {
            if (((IOperatorExpression)expr).OperatorUniqueName == "+") {
                res = Expressions.Add(res, ops[i]);
            } else if (((IOperatorExpression)expr).OperatorUniqueName == "-") {
                res = Expressions.Subtract(res, ops[i]);
            } else if (...) {
            }
        }
        return res;
    }
}

当然,在这种方法中你需要更多的逻辑。关键部分是传递参数:您需要弄清楚变量在哪里以及它们的类型是什么,Expression.ParameterExpression用来创建它,然后使用该方法将转换后的表达式编译成Func<...>某种形式。LambdaExpression.Compile使用已编译的 lambda,您可以将表达式插入到 LINQ 的内存框架中。

如果您可以使您的IExpressions可访问,请添加一个遍历表达式并使用堆栈将其转换为 LINQ 表达式的访问者。

于 2012-07-23T14:01:25.870 回答
1

您要做的是构建自己的 LINQ 查询提供程序。您可以选择要允许的 LINQ 操作(WHERE、OrderBy 等)

这是我写一篇博文时对我帮助最大的博文系列:http: //blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part- i.aspx

于 2012-07-23T13:50:55.733 回答