218

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?

4

4 回答 4

176

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

于 2009-03-20T09:47:46.803 回答
118

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.

于 2009-03-20T09:51:25.827 回答
35

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.

于 2009-03-20T10:12:05.343 回答
32

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.

于 2009-03-20T09:56:34.367 回答