C#:隐式运算符和扩展方法

我正在尝试创建一个包含Expression<Func>PredicateBuilder类,并提供一些方法来使用各种AndOr方法轻松构建表达式。 我认为如果我可以直接使用这个PredicateBuilder作为Expression<Func>会很酷,并认为这可以通过implicit operator方法来完成。

这个类的剥离版看起来像这样:

 class PredicateBuilder { public Expression<Func> Predicate { get; protected set; } public PredicateBuilder(bool initialPredicate) { Predicate = initialPredicate ? (Expression<Func>) (x => true) : x => false; } public static implicit operator Expression<Func>( PredicateBuilder expressionBuilder) { return expressionBuilder.Predicate; } } 

然后,就像测试一样,我在静态类中有这个扩展方法:

 public static void PrintExpression(this Expression<Func> expression) { Console.WriteLine(expression); } 

在我的脑海中,我应该能够做到这些:

 var p = new PredicateBuilder(true); p.PrintExpression(); PredicateExtensions.PrintExpression(p); 

但是它们都不起作用。 对于第一个,找不到扩展方法。 而对于第二个,它说

无法从用法中推断出方法’ExtravagantExpressions.PredicateHelper.PrintExpression(System.Linq.Expressions.Expression>)’的类型参数。 尝试显式指定类型参数。

所以我尝试了以下工作:

 PredicateExtensions.PrintExpression(p); 

此外,这当然有效:

 ((Expression<Func>) p).PrintExpression(); 

但是,为什么其他人不工作呢? 我是否误解了这个implicit operator的工作原理?

这不是特定于扩展方法的。 除非有关于目标类型的线索,否则C#不会隐式地将对象强制转换为其他类型。 假设如下:

 class A { public static implicit operator B(A obj) { ... } public static implicit operator C(A obj) { ... } } class B { public void Foo() { ... } } class C { public void Foo() { ... } } 

您希望在以下语句中调用哪种方法?

 new A().Foo(); // B.Foo? C.Foo? 

不,你没有,但C#编译器的类型推导不够强大,无法理解你的代码,特别是它没有看隐式运算符。 你必须坚持使用Expression> – 为什么不使用像Or这样的扩展方法,直接在表达式上?

正如Anton所说,如果将扩展方法直接放在Expression>它可能会起作用。

更多解释……没有什么特别聪明,但想法是你没有创建实例的PredicateBuilder类。 相反,你只有纯粹的静态构建块:

 public static class Predicates { public static Expression> True() { return x => true; } public static Expression> False() { return x => false; } public static Expression> And( this Expression> left, Expression> right) { return ... // returns equivalent of (left && right) } } 

这两个函数TrueFalse扮演你的PredicateBuilder(bool)构造函数的角色,你可能会有类似的原始比较等等,然后像And这样的运算符会让你把两个表达式插在一起。

但是,您失去了使用运算符符号的能力,您可以将其与包装器对象一起使用,而必须使用方法名称。 我一直在玩同样的方法,而我总是回到过的地方是我希望能够定义扩展操作符。 C#团队显然认为这些是3.0(以及扩展属性),但它们的优先级较低,因为它们没有参与Linq的整体目标。