C #: неявный оператор и методы расширения - PullRequest
11 голосов
/ 26 марта 2009

Я пытаюсь создать класс 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?

Ответы [ 3 ]

12 голосов
/ 26 марта 2009

Это не относится к методам расширения. 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? 
2 голосов
/ 26 марта 2009

Нет, у вас нет, но дедукция типа компилятора C # недостаточно мощна, чтобы понимать ваш код, и, в частности, она не учитывает неявные операторы. Вам придется придерживаться Expression<Func<T,bool>> - почему бы не использовать такие методы расширения, как Or, And непосредственно в выражениях?

0 голосов
/ 26 марта 2009

Как говорит Антон, если вы поместите методы расширения непосредственно на 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.

...