C#: implicit operator and extension methods
-
22-08-2019 - |
Question
I am trying to create a PredicateBuilder<T>
class which wraps an Expression<Func<T, bool>>
and provides some methods to easily build up an expression with various And
and Or
methods. I thought it would be cool if I could use this PredicateBuilder<T>
as an Expression<Func<T, bool>>
directly, and thought this could be done by having an implicit operator
method thing.
Stripped down version of the class looks like this:
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;
}
}
Then, just as a test, I have this extention method in a static class:
public static void PrintExpression<T>(this Expression<Func<T, bool>> expression)
{
Console.WriteLine(expression);
}
In my head, I should then be able to do these:
var p = new PredicateBuilder<int>(true);
p.PrintExpression();
PredicateExtensions.PrintExpression(p);
However none of them work. For the first one, the extension method is not found. And for the second, it says that
The type arguments for method 'ExtravagantExpressions.PredicateHelper.PrintExpression(System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
So I tried the following, which worked:
PredicateExtensions.PrintExpression<int>(p);
Also, this works, of course:
((Expression<Func<int, bool>>) p).PrintExpression();
But yeah... why don't the others work? Have I misunderstood something about how this implicit operator
thing works?
Solution
This is not specific to extension methods. C# won't implicitly cast an object to another type unless there is a clue about the target type. Assume the following:
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() { ... }
}
Which method would you expect to be called in the following statement?
new A().Foo(); // B.Foo? C.Foo?
OTHER TIPS
No, you haven't, but C# compiler's type deduction isn't powerful enough to understand your code, and in particular, it doesn't look at implicit operators. You'll have to stick with Expression<Func<T,bool>>
— why not have extension methods like Or
, And
directly on expressions?
As Anton says, if you put the extension methods directly on Expression<Func<...>>
it would probably work.
More explanation... nothing particularly clever, but the idea would be that you don't have a PredicateBuilder
class that you create instances of. Instead you just have purely static building blocks:
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)
}
}
Those two functions True
and False
play the role of your PredicateBuilder(bool)
constructor, and you'd presumably have similar ones for primitive comparisons and so on, and then operators like And
would let you plug two expressions together.
However, then you lose the ability to use operator symbols, which you could have used with your wrapper object, and instead you have to use method names. I've been playing around with the same kind of approaches, and the thing I always come back to is that I want to be able to define extension operators. The C# team apparently considered these for 3.0 (along with extension properties) but they were lower priority because they didn't play a part in the overall aims of Linq.