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


我正在努力创造一个 PredicateBuilder<T> 包装的类 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);
}

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

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 事情有效吗?


11223
2018-03-26 10:12


起源

谢谢你的清理!我一直在写扩展而不是扩展......我不知道为什么!我只是......不能......停止...... = S. - Svish


答案:


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

12
2018-03-26 10:48



我会说两个。不,猜猜这是一个观点,呵呵。 - Svish
我希望“Foo()含糊不清:B.Foo()或C.Foo()” - Anton Tykhyy
@Anton:这是可能的,但会使语言复杂化并可能隐藏副作用。毕竟,当你在一个类上定义一个新的隐式运算符时,你觉得如果一个工作代码突然中断,你会怎么样?在任何地方强制显式类型声明都比较简单。 - Mehrdad Afshari
如果你只处理两个班级,它将无法工作(A 和 B)或者。删除 C 完全从你的例子中的类仍然给出错误 “CS1061:类型'A'不包含'Foo'的定义,也没有找到'A'类型的扩展方法'Foo'(你是否缺少using指令或汇编引用?)”。你需要得到 implicit在你进入多个不同的类之前,用例是正确的。 - Slipp D. Thompson
@ SlippD.Thompson这不是重点。这段代码假设C#以某种方式支持该功能,展示了它将导致的问题。当然它不起作用。 - Mehrdad Afshari


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


2
2018-03-26 10:16



啊哈,好吧,技术上它应该工作,只是它不会在隐式运算符类型的东西上寻找扩展方法? - Svish
我会说“理想”它应该有用:)就这样,它不够强大,不知道在哪里看。 - Anton Tykhyy
已经有这些扩展方法。但是在构建像这样的表达式时,我正试图让事情变得更容易。不确定我是否会成功:p - Svish
祝你好运^。^ - Anton Tykhyy


正如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 和 False 发挥你的作用 PredicateBuilder(bool) 构造函数,你可能有类似的原始比较等等,然后运算符喜欢 And 会让你把两个表达式插在一起。

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


0
2018-03-26 10:31



你把“扩展方法直接放在上面是什么意思? Expression<Func<...>>“?这不是我已经做过的吗?或者你的意思是用它做什么? Expression<Func<T, bool>> 而不是在 PredicateBuilder<T>? (这只是正常使用,当然有效) - Svish
见解释。 - Daniel Earwicker
我不明白...... - Svish
哦,伙计,我发布的东西从未出现过!对于那个很抱歉。 - Daniel Earwicker
它现在在那里。希望在经过这么长时间的建立之后不会感到失望! - Daniel Earwicker