14

我正在尝试创建一个PredicateBuilder<T>包装 an并Expression<Func<T, bool>>提供一些方法来轻松构建具有各种方法的表达式的类。我认为如果我可以直接使用它会很酷,并且认为这可以通过有一个方法来完成。AndOrPredicateBuilder<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);
}

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

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

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

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

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

所以我尝试了以下方法,它有效:

PredicateExtensions.PrintExpression<int>(p);

当然,这也有效:

((Expression<Func<int, bool>>) p).PrintExpression();

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

4

3 回答 3

15

这并不特定于扩展方法。除非有关于目标类型的线索,否则 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? 
于 2009-03-26T10:48:20.400 回答
2

不,您没有,但是 C# 编译器的类型推导功能不足以理解您的代码,尤其是它不考虑隐式运算符。你必须坚持Expression<Func<T,bool>>——为什么不直接在表达式上使用像Or,这样的扩展方法呢?And

于 2009-03-26T10:16:27.870 回答
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)
    }
}

这两个函数扮演你的构造函数的角色,True你可能会有类似的原始比较等,然后运算符会让你将两个表达式连接在一起。FalsePredicateBuilder(bool)And

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

于 2009-03-26T10:31:30.707 回答