这是我的界面和枚举,略微笨拙。 :
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
或其他的实现,但我不知道从哪里开始。有什么建议吗?
答案 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