OrderBy keySelector with different data types on TKey

Viewed 229

I need a method that returns a keySelector, which can be used when invoking an OrderBy expression. The problem is that a keySelector is a generic delegate and I would like the method to return keySelectors with different data types on TKey:

Func<TSource,TKey> keySelector

I would like to accomplish this, but instead of using lambda expressions a method that returns a keySelector (TKey=string or int):

IEnumerable<Person> orderedByFirstName = persons.OrderBy(p => p.FirstName);
IEnumerable<Person> orderedByAge = persons.OrderBy(p => p.Age);

Is it possible to have a method that returns keySelectors with different types on TKey in some way? It isn't possible to return lambda expressions, but something like that...

As long as the method only returns keySelectors of data type string everything works fine:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

public class Behavior
{
    public virtual string GetOrderByKeySelector(Person p)
    {
        return p.FirstName;
    }
}

public class SpecializedBehavior : Behavior
{
    public override string GetOrderByKeySelector(Person p)
    {
        return p.LastName;
    }
}

List<Person> persons = new List<Person>
{
    new Person{Age=10, FirstName="Pelle", LastName="Larsson"},
    new Person{Age=90, FirstName="Nils", LastName="Nilsson"}, 
    new Person{Age=15, FirstName="Olle", LastName="Johansson"},
    new Person{Age=30, FirstName="Kalle", LastName="Svensson"}
};

Used like this:

SpecializedBehavior behavior = new SpecializedBehavior();
IEnumerable<Person> orderedResult = persons.OrderBy(behavior.GetOrderByKeySelector);
2 Answers

How about taking in the collection to the method instead.

void Main()
{
    SpecializedBehavior behavior = new SpecializedBehavior();
    IEnumerable<Person> orderedResult = behavior.Sort(persons);
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

public class Behavior
{
    public virtual IEnumerable<Person> Sort(IEnumerable<Person> persons)
    {
        return persons.OrderBy(p => p.FirstName);
    }
}

public class SpecializedBehavior : Behavior
{
    public override IEnumerable<Person> Sort(IEnumerable<Person> persons)
    {
        return persons.OrderBy(p => p.Age);
    }
}


List<Person> persons = new List<Person>
{
    new Person{Age=10, FirstName="Pelle", LastName="Larsson"},
    new Person{Age=90, FirstName="Nils", LastName="Nilsson"},
    new Person{Age=15, FirstName="Olle", LastName="Johansson"},
    new Person{Age=30, FirstName="Kalle", LastName="Svensson"}
};

This answer is just to illustrate @LasseVågsætherKarlsen's comment and all the credit should go to him:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

public class PersonByFirstNameComparer : IComparer<Person>
{
    public int Compare(Person left, Person right) =>
        left.FirstName.CompareTo(right.FirstName);
}

public class Behavior
{
    public virtual IComparer<Person> GetComparer() => PersonByFirstNameComparer();
}

public class PersonByLastNameComparer : IComparer<Person>
{
    public int Compare(Person left, Person right) =>
        left.LastName.CompareTo(right.LastName);
}

public class SpecializedBehavior : Behavior
{
    public override IComparer<Person> GetComparer() => new PersonByLastNameComparer();
}

which can be used the following way:

public static void Main()
{
    var people = new List<Person>
    {
        new Person{Age=10, FirstName="Pelle", LastName="Larsson"},
        new Person{Age=90, FirstName="Nils", LastName="Nilsson"}, 
        new Person{Age=15, FirstName="Olle", LastName="Johansson"},
        new Person{Age=30, FirstName="Kalle", LastName="Svensson"}
    };

    var standardBehavior = new Behavior();
    IEnumerable<Person> orderedPeople1 = 
        people.OrderBy(p => p, standardBehavior.GetComparer());

    foreach (Person p in orderedPeople1)
    {
        Console.WriteLine($"{p.FirstName} {p.LastName}");
    }

    var specializedBehavior = new SpecializedBehavior();
    IEnumerable<Person> orderedPeople2 = 
        people.OrderBy(p => p, specializedBehavior.GetComparer());

    foreach (Person p in orderedPeople2)
    {
        Console.WriteLine($"{p.FirstName} {p.LastName}");
    }
}

Link to fiddle

Related