Ra'ed Alaraj
Ra'ed Alaraj

Reputation: 173

How to pass lambda expression to where statement with comparison

Let's say I have this class

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

And I want to write a method that can return a list of users based on the property of the Person and the comparison filed.

private List<Person> GetResult(Func<Person, object> p, string v)
{
       List<Person> data = GetResultsFromDb();
       return data.Where(p == v);
}

For example, if I want to get all the People who are called Mike, I can say

var result = GetResult(a => a.Name, "Mike");

Or if I want to get results based on phone number I can say

var result = GetResult(a => a.PhoneNumber, 123);

However I'm getting

Operator == cannot be applied to operands of type 'Expression<Func<Person, object>>' and string

Upvotes: 2

Views: 882

Answers (2)

Sefe
Sefe

Reputation: 14007

The argument of p is a delegate. This type does not compare well with a string (hence your error message). You need to call the delegate to get a value. However, a return type of object is not optimal for a comparison. So you should make your method generic. Now that you are generic, you can't use the equality operator ==, but you can use Equals. And finally you need to call ToList to match the return type of the method:

private List<Person> GetResult<T>(Func<Person, T> p, T v)
{
       List<Person> data = GetResultsFromDb();
       return data.Where(item => p(item).Equals(v)).ToList();
}

Since the parameter v is of type T, T can be inferred when you call the method. That means you can omit the type parameter, so you can use GetResult(a => a.Name, "Mike") instead of GetResult<string>(a => a.Name, "Mike").

Some .NET Pros could complain now that you don't need the method to be generic when you call Equals, since Equals is defined on Object. You could also point out the fact that comparison of null values will not work and that an IEquatable implementations are not given any special consideration. If you want to dodge all of these complaints, you can use an EqualityComparer for the comparison:

private List<Person> GetResult<T>(Func<Person, T> p, T v)
{
       List<Person> data = GetResultsFromDb();
       return data.Where(item => EqualityComparer<T>.Default.Equals(p(item), v)).ToList();
}

Upvotes: 4

jdweng
jdweng

Reputation: 34421

See code below. You had p being used for two different purposes

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

        private List<Person> GetResult(Func<Person, object> p, string v)
        {
            List<Person> data = GetResultsFromDb();
            return data.Where(x => x.Name == v).ToList();
        }

        private List<Person> GetResultsFromDb()
        {
            return new List<Person>() {
                new Person() { Name = "Mike"},
                new Person() { Name = "John"},
                new Person() { Name = "Mike"}
            };
        }

    }

Upvotes: -2

Related Questions