使用自定义方法构造AndAlso / OrElse LINQ表达式

时间:2017-04-16 05:35:42

标签: c# .net linq expression-trees

我尝试将自定义MethodInfo传递给Expression.AndAlsoOrElse工厂方法(适用于&&||分别是运营商)。这些运算符使用短路,这使得这很困难,但通常使用&|运算符(以及truefalse运算符)。但是,Expression.AndAlso/OrElse的MSDN文档并未提及真或假运算符。

为了进行测试,我宣布了一种在两个整数中使用普通&运算符的方法:

public static int And(int a, int b) {
    return a & b;
}

请注意,返回类型必须为int而不是bool,以避免出现异常 然后我构造表达式:

var expr = Expression.AndAlso(
    Expression.Constant(0),
    Expression.Constant(5),
    new Func<int, int, int>(And).Method
);

这会导致异常:

  

用户定义的运算符方法&#39;和&#39;对于运营商而言,还有其他&#39;必须有关联的布尔值True和False运算符。

奇怪的是,如果我使用具有truefalse运算符的自定义结构,也会引发错误。如果struct重载&运算符并且传入了该重载,我可以避免它,但是如果我传入一个不同的方法则不会。其他非短路运算符也可以使用自定义方法。

问题在于我不知道如何为truefalse运算符传递方法。我首先想到我可以将它们组合为代理,但不同的方法有不兼容的签名。有没有办法传递这些方法?

更大的图片

我正在构建一个用于解释表达式的系统,以支持提前编译。它支持为AndAlso / OrElse运算符使用自定义方法,目前通过采用自定义Func<InterpretedExpression, InterpretedExpression, object>(在表达式被解释而不是编译时起作用)。如果它导致问题(这可能是因为它没有可访问的truefalse方法),这很容易被更改。

注意:我使用Visual Studio的C#Interactive窗口进行测试,但最终需要支持.NET 3.5(尽管有关新版本的信息仍然有用且值得赞赏)。

1 个答案:

答案 0 :(得分:3)

  

问题在于我不知道如何为真假运算符传递方法。我首先想到我可以将它们组合为委托,但不同的方法具有不兼容的签名。有没有办法传递这些方法?

简短的回答是不,你不能。

查看reference source implementation(遗憾的是,它应该在文档中),看起来传递的方法有以下约束:

(1)它应该是带有签名的static 非通用方法

static T Method(T arg0, T arg1);

其中T不能是开放泛型类型

(2)方法的声明类型必须定义运算符truefalse

由于运算符truefalse要求参数类型与声明类型相同,这与(1)实际上限制了对类/结构T的使用的声明静态方法以及truefalse运算符。

换句话说,一个按位& / |运算符语义而不实际重载这些运算符的方法。

所以它只能用于这样的类型:

public struct IntWrapper
{
    public readonly int Value;
    public IntWrapper(int value) { Value = value; }
    public static IntWrapper And(IntWrapper a, IntWrapper b) { return new IntWrapper(a.Value & b.Value); }
    public static bool operator true(IntWrapper x) { return x.Value != 0; }
    public static bool operator false(IntWrapper x) { return x.Value == 0; }
}

用法:

var expr = Expression.AndAlso(
    Expression.Constant(new IntWrapper(0)),
    Expression.Constant(new IntWrapper(5)),
    new Func<IntWrapper, IntWrapper, IntWrapper>(IntWrapper.And).Method
);

我想这限制了你所追求的更普遍的用法。