Why Func<T,bool> instead of Predicate<T>?
Asked Answered
D

4

234

This is just a curiosity question I was wondering if anyone had a good answer to:

In the .NET Framework Class Library we have for example these two methods:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

Why do they use Func<TSource, bool> instead of Predicate<TSource>? Seems like the Predicate<TSource> is only used by List<T> and Array<T>, while Func<TSource, bool> is used by pretty much all Queryable and Enumerable methods and extension methods... what's up with that?

Deterioration answered 20/3, 2009 at 9:39 Comment(0)
G
187

While Predicate has been introduced at the same time that List<T> and Array<T>, in .net 2.0, the different Func and Action variants come from .net 3.5.

So those Func predicates are used mainly for consistency in the LINQ operators. As of .net 3.5, about using Func<T> and Action<T> the guideline states:

Do use the new LINQ types Func<> and Expression<> instead of custom delegates and predicates

Guimpe answered 20/3, 2009 at 9:47 Comment(4)
Well, actually you have to place that in the context of the guideline, which is about writing a LINQ provider. So yeah, for LINQ operators, the guideline is to use Func<> and Expression<> as parameters for the extension methods. I agree I'd appreciate a separate guideline about using Func and ActionGuimpe
The point of the guideline is to say that when implementing LINQ operators, one should use Func<> and Expression<Func<>>.Guimpe
The guideline talks about custom delegates and predicates and gives an example of defining a custom delegate bool Tester(int i). System.Predicate<T> is not custom.Verrocchio
@Verrocchio Was just about to say the same thing. The guideline linked to is not an argument against using Predicate<T> (unless you consider Predicate<T> to be a custom delegate).Slowly
T
129

I've wondered this before. I like the Predicate<T> delegate - it's nice and descriptive. However, you need to consider the overloads of Where:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

That allows you to filter based on the index of the entry as well. That's nice and consistent, whereas:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

wouldn't be.

Tugboat answered 20/3, 2009 at 9:51 Comment(0)
W
37

Surely the actual reason for using Func instead of a specific delegate is that C# treats separately declared delegates as totally different types.

Even though Func<int, bool> and Predicate<int> both have identical argument and return types, they are not assignment-compatible. So if every library declared its own delegate type for each delegate pattern, those libraries would not be able to interoperate unless the user inserts "bridging" delegates to perform conversions.

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

By encouraging everyone to use Func, Microsoft is hoping that this will alleviate the problem of incompatible delegate types. Everyone's delegates will play nicely together, because they will just be matched up based on their parameter/return types.

It doesn't solve all problems, because Func (and Action) can't have out or ref parameters, but those are less commonly used.

Update: in the comments Svish says:

Still, switching a parameter type from Func to Predicate and back, doesn't seem to make any difference? At least it still compiles without any problems.

Yes, as long as your program only assigns methods to delegates, as in the first line of my Main function. The compiler silently generates code to new a delegate object that forwards on to the method. So in my Main function, I could change x1 to be of type ExceptionHandler2 without causing a problem.

However, on the second line I try to assign the first delegate to another delegate. Even thought that 2nd delegate type has exactly the same parameter and return types, the compiler gives error CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'.

Maybe this will make it clearer:

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

My method IsNegative is a perfectly good thing to assign to the p and f variables, as long as I do so directly. But then I can't assign one of those variables to the other.

Wodge answered 20/3, 2009 at 10:12 Comment(3)
Still, switching a parameter type from Func<T, bool> to Predicate<T> and back, doesn't seem to make any difference? At least it still compiles without any problems.Deterioration
Seems MS are trying to discourage developers from thinking these are the same. I wonder why?Indispose
Switching the parameter type does make a difference if the expression you are passing to it has been defined separately to the method call, since then it will be typed as either Func<T, bool> or Predicate<T> rather than having the type inferred by the compiler.Gurango
D
35

The advice (in 3.5 and above) is to use the Action<...> and Func<...> - for the "why?" - one advantage is that "Predicate<T>" is only meaningful if you know what "predicate" means - otherwise you need to look at object-browser (etc) to find the signatute.

Conversely Func<T,bool> follows a standard pattern; I can immediately tell that this is a function that takes a T and returns a bool - don't need to understand any terminology - just apply my truth test.

For "predicate" this might have been OK, but I appreciate the attempt to standardise. It also allows a lot of parity with the related methods in that area.

Divine answered 20/3, 2009 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.