代码之家  ›  专栏  ›  技术社区  ›  Jesus is Lord

我有一个自定义的表达式程序,包含lexer、解析器和计算器。我该如何对其进行LINQ验证?

  •  4
  • Jesus is Lord  · 技术社区  · 12 年前

    以下是我的接口和枚举,稍微降低了一些。:

    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 或者什么的,但我不确定从哪里开始。有什么建议吗?

    2 回复  |  直到 12 年前
        1
  •  3
  •   Sergey Kalinichenko    12 年前

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

    如果您无法修改 IExpression 类来添加方法,使您能够实现 visitor pattern ,您可以依靠类型检查旧样式:

    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 visible,添加一个访问者来遍历表达式并使用堆栈将其转换为LINQ表达式。

        2
  •  1
  •   James    12 年前

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

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