我有一个带有词法分析器,解析器和评估器的自定义表达式程序。如何LINQ-ify呢?

时间:2012-07-23 13:47:42

标签: c# linq iqueryable

这是我的界面和枚举,略微笨拙。 :

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 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

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

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