为何List<T>.FindAll()仅接受Predicate<TSource>而非Func<TSource, bool>?
List<T>.FindAll() Accept Func<TSource, bool>? Great question—this is one of those .NET quirks that boils down to historical context and how delegate types work in C#. Let's break it down clearly:
The Core Issue: Delegate Type Compatibility
Even though Func<TSource, bool> and Predicate<TSource> have identical signatures (both take a TSource parameter and return a bool), they are completely separate delegate types in .NET. C# doesn't allow implicit conversion between different delegate types—even if their method signatures match. That's exactly why you're seeing that frustrating error:
Argument 1: cannot convert from 'System.Func' to 'System.Predicate'
The compiler sees them as distinct types, so it won't automatically bridge the gap for you.
The Historical Backstory
Here's why this mismatch exists in the first place:
List<T>launched in .NET 2.0, alongside thePredicate<TSource>delegate. Back then, there was no genericFunc<...>delegate family—those didn't arrive until .NET 3.5, when LINQ was added to the framework.- Microsoft created
Predicate<TSource>specifically for List's filtering methods (like FindAll,Exists,Find) because there was no general-purpose generic delegate for boolean-returning functions yet. - By the time
Func<TSource, bool>came around,List<T>was already widely used in existing code. Changing the parameter type toFuncwould have broken backward compatibility for thousands of projects, so Microsoft left the methods as-is.
How to Work Around It
You have a couple of straightforward options:
1. Explicitly Convert Func to Predicate
You can explicitly create a Predicate<TSource> instance from your lambda, or cast the lambda directly:
// Option A: Explicitly instantiate Predicate List<Book> cheapBooks = books.FindAll(new Predicate<Book>(book => book.Price < 10)); // Option B: Cast the lambda directly List<Book> cheapBooks = books.FindAll((Predicate<Book>)(book => book.Price < 10));
2. Use LINQ's Where Method (Recommended)
If you're using .NET 3.5 or later, skip FindAll entirely and use LINQ's Where method—it accepts Func<TSource, bool> natively. Just remember to add using System.Linq; and call ToList() to get a List<T> result:
using System.Linq; List<Book> cheapBooks = books.Where(book => book.Price < 10).ToList();
This is often the cleaner approach, especially if you're already using other LINQ methods in your code.
内容的提问来源于stack exchange,提问作者Nashmár

