Lambda works in FindAll, but not when using it as

2019-08-11 21:45发布

问题:

The code below won't compile:

Func<Person, bool> theLambda = (p) => p.Year >= 1992;

foreach (Person pers in PersonList.FindAll(theLambda))
{
    Console.WriteLine(pers.Name);
}

public class Person
{
    public string Name { get; set; }
    public int Year { get; set; }

    public Person(string Name, int Year )
    {
        this.Name = Name; this.Year = Year;
    }
}  

However, if I replace variable "theLambda" directly with the lambda, then it works just fine. What's going on here? (Be gentle, I'm a novice). Thank you so much in advance!
(1) I read the error message, but it doesn't mean anything to me.
(2) Yes, I can make it work with a Predicate by using the compile() keyword, but that's not the issue here.

Edit: why would anyone downvote this? The question wasn't that bad at all as the problem domain is not of a logic nature indeed. Really people.

回答1:

It works because if you declare the lambda inline the compiler implicitly assigns it the right type, i.e. Predicate<Person>. You don't have to explicitly tell the compiler the lambda type as it knows already that it should take a Person and return a bool if you call FindAll on a List<Person>.

foreach (Person pers in PersonList.FindAll(p => p.Year >= 1992))
{
    Console.WriteLine(pers.Name);
}

You can also use Enumerable.Where - LINQ method with the same functionality to make it a bit more readable:

foreach (Person pers in PersonList.Where(p => p.Year >= 1992))
{
    Console.WriteLine(pers.Name);
}

From msdn:

When writing lambdas, you often do not have to specify a type for the input parameters because the compiler can infer the type based on the lambda body, the parameter’s delegate type, and other factors as described in the C# Language Specification. For most of the standard query operators, the first input is the type of the elements in the source sequence. So if you are querying an IEnumerable<Customer>, then the input variable is inferred to be a Customer object

The confusing part is that a Predicate is logically a Func that takes an object of some type T and returns a bool, but for some reason this typing doesn't work and you have to use Predicate<T>. Declaring the lambda function inline avoids this confusion as you just write the lambda body and let the compiler infer the type on its own.



回答2:

The FindAll expects a Predicate and not a Function as seen in the method definition Array.FindAll<T> Method (T[], Predicate<T>)

When you try to pass theLambda it is trying to pass a Function, when the method expects a Predicate. You can instead try defining theLambda as

Predicate<Person> theLambda = (p) => p.Year >= 1992;

A Predicate is a Function that returns a Boolean and that is what is required by the FindAll method to filter the results.



回答3:

Based on the answer here, you can do the following.

foreach (Person pers in PersonList.FindAll(new Predicate<Person>(theLambda)))