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

时间:2009-03-26 10:12:01

标签: c# operator-overloading extension-methods

我正在尝试创建一个包含PredicateBuilder<T>的{​​{1}}类,并提供一些方法来轻松地使用各种Expression<Func<T, bool>>And方法构建表达式。我认为如果我可以直接使用Or作为PredicateBuilder<T>,那会很酷,并认为这可以通过Expression<Func<T, bool>>方法来完成。

该类的精简版本如下所示:

implicit operator

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

class PredicateBuilder<T>
{
    public Expression<Func<T, bool>> Predicate { get; protected set; }

    public PredicateBuilder(bool initialPredicate)
    {
        Predicate = initialPredicate 
            ? (Expression<Func<T, bool>>) (x => true) 
            : x => false;
    }

    public static implicit operator Expression<Func<T, bool>>(
        PredicateBuilder<T> expressionBuilder)
    {
        return expressionBuilder.Predicate;
    }
}

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

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

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

  

无法从用法推断出方法'ExtravagantExpressions.PredicateHelper.PrintExpression(System.Linq.Expressions.Expression&gt;)'的类型参数。尝试明确指定类型参数。

所以我尝试了以下工作:

var p = new PredicateBuilder<int>(true);

p.PrintExpression();
PredicateExtensions.PrintExpression(p);

此外,这当然有效:

PredicateExtensions.PrintExpression<int>(p);

但是,为什么其他人不工作呢?我是否误解了这个((Expression<Func<int, bool>>) p).PrintExpression(); 的工作原理?

3 个答案:

答案 0 :(得分:12)

这不是特定于扩展方法的。除非有关于目标类型的线索,否则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? 

答案 1 :(得分:2)

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

答案 2 :(得分:0)

正如Anton所说,如果你将扩展方法直接放在Expression<Func<...>>上,它可能会有用。

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

public static class Predicates
{
    public static Expression<Func<T, bool>> True<T>()
    {
        return x => true;
    }

    public static Expression<Func<T, bool>> False<T>()
    {
        return x => false;
    }

    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> left,
        Expression<Func<T, bool>> right)
    {
        return ... // returns equivalent of (left && right)
    }
}

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

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