С#: неявный оператор и методы расширения
Я пытаюсь создать класс 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.Expressionions.Expression > )" не могут быть выведены из использования. Попробуйте явно указать аргументы типа.
Итак, я попробовал следующее:
PredicateExtensions.PrintExpression<int>(p);
Кроме того, это работает, конечно:
((Expression<Func<int, bool>>) p).PrintExpression();
Но да... почему другие не работают? Я что-то неправильно понял о том, как это работает implicit operator
?
Ответы
Ответ 1
Это не относится к методам расширения. С# не будет неявным образом передавать объект другому типу, если не будет указаний относительно целевого типа. Предположим следующее:
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
Нет, вы этого не сделали, но выведение типа компилятора С# недостаточно мощно, чтобы понять ваш код, и, в частности, он не смотрит на неявные операторы. Вам придется придерживаться Expression<Func<T,bool>>
- почему бы не использовать методы расширения, такие как Or
, And
непосредственно в выражениях?
Ответ 3
Как говорит Антон, если вы поместите методы расширения непосредственно на 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
, могли бы подключить два выражения вместе.
Однако, вы теряете возможность использовать символы оператора, которые вы могли бы использовать с вашим объектом-оболочкой, и вместо этого вы должны использовать имена методов. Я играл с такими же подходами, и я всегда возвращался к тому, что хочу иметь возможность определять операторы расширения. Команда С#, по-видимому, рассматривала их для 3.0 (наряду с свойствами расширения), но они были более низкими приоритетами, поскольку они не играли роли в общих целях Linq.