views:

1969

answers:

3

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 doesn't the others work? Have I misunderstood something about how this implicit operator thing works?

+1  A: 

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?

Anton Tykhyy
aha, ok, so technically it should work, it is just that it doesn't look for extention methods on implicit operator type stuff?
Svish
I'd say "ideally" it should work :) Just so, it's not powerful enough to know where to look.
Anton Tykhyy
Already have those extension methods. But I'm trying to make things a bit easier to work with when building up expressions like this. Not sure if I will succeed though :p
Svish
Good luck anyway ^.^
Anton Tykhyy
A: 

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.

Daniel Earwicker
What do you mean by "putting the extension method directly on `Expression<Func<...>>`"? Isn't that what I have already done? Or do you mean using it on an `Expression<Func<T, bool>>` instead of on a `PredicateBuilder<T>`? (That would just be normal usage, and works of course)
Svish
See explanation.
Daniel Earwicker
I don't get it...
Svish
Oh man, the thing I posted never appeared! Sorry about that.
Daniel Earwicker
It's there now. Hope it's not a disappointment after such a long build up!
Daniel Earwicker
lol. no worries! Anyways, those are kind of the methods I already have as extension methods. And, Or and a couple of other (hopefully) handy methods.
Svish
+2  A: 

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 you'd expect to be called in the following statement?

new A().Foo(); // B.Foo? C.Foo?
Mehrdad Afshari
I would say both. Nah, guess that is a point, hehe.
Svish
I would expect "Foo() is ambiguous: B.Foo() or C.Foo()"
Anton Tykhyy
@Anton: That is possible, but would complicate the language and possibly hiding the side effects. And after all, how'd you feel if a working code suddenly breaks when you define a new implicit operator on a class :) It's just simpler to force the explicit type declaration everywhere.
Mehrdad Afshari