How does Func<T,TResult> Work?
Asked Answered
R

3

20

I am creating a Distinct extension method where I can pass in the criteria like the following.

persons.Distinct(p => p.Name); 

I got the code from the web but I am having a hard time understanding the purpose of Func<T, TResult>. Also, when I say p => p.Name am I sending the String Name or am I sending the complete Person object? Here is the new Distinct method:

public static class ExtensionMethods
{
    public static IEnumerable<T> Distinct<T>(
        this IEnumerable<T> list, Func<T,object> checker)
    {
        return list.Distinct(new GenericComparer<T>(checker)); 
    }
}

public class GenericComparer<T> : IEqualityComparer<T>
{
    private Func<T, object> _checker; 

    public GenericComparer(Func<T,object> checker)
    {
        _checker = checker; 
    }

    public bool Equals(T x, T y)
    {
        return _checker(x).Equals(_checker(y));
    }

    public int GetHashCode(T obj)
    {
        return _checker(obj).GetHashCode(); 
    }
}

And here is the usage:

static void Main(string[] args)
{
    var persons = new List<Person>()
    {
        new Person() { Id = 1, Name = "Mary"}, 
        new Person() {Id = 2, Name="John"}, 
        new Person() { Id = 3, Name = "Mary"}
    };

    var uniquePersons = persons.Distinct(p => p.Name); 

    foreach(var person in uniquePersons)
    {
        Console.WriteLine(person.Name);
    }
}
Robbie answered 31/7, 2009 at 16:23 Comment(0)
B
35

When you do this:

persons.Distinct(p => p.Name);

You're basically creating a function on the fly (using lambda expressions), that looks like this:

string theFunction(Person p)
{
    return p.Name;
}

This is a function that fits the signature of a Func<Person,String> delegate. The Distinct method can take a delegate (basically a function pointer) which it uses to determine whether or not an element is distinct - in your case, only unique strings (returned by the function above) will be considered "distinct" elements. This delegate is run on each element of your "persons" enumerable, and the results of those functions are used. It then creates a sequence (IEnumerable<Person>) from those elements.

Bielefeld answered 31/7, 2009 at 16:27 Comment(3)
+1, but "In your case, an element is distinct if no other element has the same Name. If more than one item in the source has the same name, only the first of them will be present in the result." Also, no overload of the Distict() method takes a selector - did you write your own?Bellew
Yes, I got a Distinct extension method of the internet! I did not write it myself!Robbie
func is just a shorthand of writing a complete function. Internally it create an opaque function and compiler use this opaque function instead of func.Pulmonic
S
10
Func<T, TResult>

defines a function that accepts one parameter (of type T) and returns an object (of type TResult).

In your case, if you want a function that takes a Person object and returns a string...you'd want

Func<Person, string>

which is the equivalent of:

string Function(Person p)
{
    return p.Name;
}
Stoneware answered 31/7, 2009 at 16:30 Comment(0)
E
0

You are getting back the distinct People, under the assumption that two People are the same if they have the same name

If you want a distinct set of names, you can use this:

IEnumerable<String> names = persons.Select(p => p.Name).Distinct();
Engelhart answered 31/7, 2009 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.