【问题标题】:C#: implicit operator and extension methodsC#:隐式运算符和扩展方法
【发布时间】:2010-10-15 16:05:03
【问题描述】:

我正在尝试创建一个 PredicateBuilder<T> 类,它包装了一个 Expression<Func<T, bool>> 并提供了一些方法来轻松地使用各种 AndOr 方法构建表达式。我认为如果我可以直接将这个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 的工作原理?

【问题讨论】:

    标签: c# operator-overloading extension-methods


    【解决方案1】:

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

    【讨论】:

    • aha,好的,所以从技术上讲它应该可以工作,只是它不会在隐式运算符类型的东西上寻找扩展方法?
    • 我会说“理想情况下”它应该可以工作 :) 只是这样,它还不够强大,无法知道在哪里看。
    • 已经有了这些扩展方法。但是在构建这样的表达式时,我试图让事情变得更容易处理。不知道我是否会成功:p
    【解决方案2】:

    正如 Anton 所说,如果您将扩展方法直接放在 Expression&lt;Func&lt;...&gt;&gt; 上,它可能会起作用。

    更多解释......没有什么特别聪明的,但想法是你没有创建实例的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) 构造函数的角色,你可能会有类似的函数用于原始比较等等,然后像 And 这样的运算符会让你插入两个一起表达。

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

    【讨论】:

    • “把扩展方法直接放在Expression&lt;Func&lt;...&gt;&gt;上”是什么意思?这不是我已经做过的吗?还是您的意思是在Expression&lt;Func&lt;T, bool&gt;&gt; 而不是PredicateBuilder&lt;T&gt; 上使用它? (这只是正常用法,当然可以)
    • 它现在就在那里。希望经过这么长时间的积累不会让人失望!
    • 哈哈。不用担心!无论如何,这些都是我已经拥有的扩展方法。而且,或者还有其他几个(希望是)方便的方法。
    【解决方案3】:

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

    【讨论】:

    • 我希望“Foo() 不明确:B.Foo() 或 C.Foo()”
    • @Anton:这是可能的,但会使语言复杂化并可能隐藏副作用。毕竟,当你在一个类上定义一个新的隐式运算符时,如果工作代码突然中断,你会有什么感觉:) 在任何地方强制显式类型声明会更简单。
    • 如果你只处理两个类(AB),它也不会工作。从您的示例中完全删除C 类仍然会给出错误“CS1061:类型'A'不包含'Foo'的定义并且找不到'A'类型的扩展方法'Foo'(是您缺少 using 指令或程序集引用吗?)”。您需要正确处理 implicit 的用例,然后才能将其转换为多个不同的类。
    • @SlippD.Thompson 这不是重点。此代码假设 C# 以某种方式支持该功能,展示了它将导致的问题。当然不行。
    • 那么我建议将您的代码更新为支持您观点的内容。目前所写的内容具有误导性。
    猜你喜欢
    • 2014-07-31
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-20
    • 1970-01-01
    • 2010-10-27
    相关资源
    最近更新 更多