0

I am struggling to understand why does not List<T>FindAll(...) method accepts Func<TSource, bool> but instead insists on accepting Predicate<TSource>.

So when I have a List of books and I want to get only books, which are cheaper than 10. This code runs just fine.

  Predicate<Book> CheapBooksPredicate = b => b.Price < 10;
  var cheapBooksPredicate = books.FindAll(CheapBooksPredicate);

But when I change Predicate<TSource> to Func<TSource, bool>

   Func<Book, bool> CheapBooksFunc = b => b.Price < 10;
   var cheapBooksFunc = books.FindAll(CheapBooksFunc);

I am getting error:

Argument 1: cannot convert from 'System.Func' to 'System.Predicate'

What Am I missing here ? When both Func<TSource, bool> and Predicate<TSource> are predicates. Predicate<TSource> should be specialized version of a Func that takes and evaluates a value against a set of criteria and returns a boolean, thus I though, that they can replace each other in terms of usage.

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Nashmár
  • 392
  • 1
  • 7
  • 22
  • The definition of FindAll predates the definition of the `Func<>` family of delegates, so they made their own delegate type for their specific case. Delegates types, like all other types in .NET, are not assignment compatible because of structural similarity. The name of the type matters. – Mike Zboray Mar 11 '18 at 22:09
  • Just use LINQ: `.Where(...)` – Ian Mercer Mar 11 '18 at 22:12
  • @mikez So even if both do the same, but they have a different name, compiler takes them as two different types ? – Nashmár Mar 11 '18 at 23:24
  • @Nashmár Yes. You might get more insight from [this question](https://stackoverflow.com/questions/4467412/cannot-assign-a-delegate-of-one-type-to-another-even-though-signature-matches), especially the comments by Eric Lippert and his answer [here](https://stackoverflow.com/q/2488643/517852). In short, if the compiler/runtime team had to do it all over again, they would probably choose to implement structural typing for delegates, but as of now that would be a breaking change which means it is unlikely to happen. – Mike Zboray Mar 11 '18 at 23:41
  • @mikez I think I got it now. Thanks. – Nashmár Mar 12 '18 at 10:33

2 Answers2

5

They have the same signature, but they are fundamentally different types and cannot be cast as a reference-preserving conversion. Since FindAll wants a Predicate<T>: use Predicate<T>.

Would it be nice if they were castable like this? Maybe, but it would require CLR and language changes, and is unlikely to happen.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

You can use the following Extension method to convert Func<T, bool> to Predicate<T>

static Predicate<T> FuncToPredicate<T>(Func<T, bool> func)
{
   return new Predicate<T>(func);
}

Reference

M.Hassan
  • 10,282
  • 5
  • 65
  • 84