Ταξινόμηση μιας λίστας με χρήση Lambda/Linq σε αντικείμενα

Έχω το όνομα της ιδιότητας "sort by" σε μια συμβολοσειρά. Θα πρέπει να χρησιμοποιήσω Lambda/Linq για να ταξινομήσω τη λίστα των αντικειμένων.

Πχ:

public class Employee
{
  public string FirstName {set; get;}
  public string LastName {set; get;}
  public DateTime DOB {set; get;}
}

public void Sort(ref List<Employee> list, string sortBy, string sortDirection)
{
  //Example data:
  //sortBy = "FirstName"
  //sortDirection = "ASC" or "DESC"

  if (sortBy == "FirstName")
  {
    list = list.OrderBy(x => x.FirstName).toList();    
  }

}
  1. Αντί να χρησιμοποιείτε ένα σωρό ifs για να ελέγχετε το όνομα του πεδίου (sortBy), υπάρχει ένας καθαρότερος τρόπος για να κάνετε την ταξινόμηση
  2. Γνωρίζει η ταξινόμηση τον τύπο δεδομένων;

Ένα πράγμα που θα μπορούσατε να κάνετε είναι να αλλάξετε το Sort έτσι ώστε να κάνει καλύτερη χρήση των lambdas.

public enum SortDirection { Ascending, Descending }
public void Sort(ref List list,
                       Func sorter, SortDirection direction)
{
  if (direction == SortDirection.Ascending)
    list = list.OrderBy(sorter);
  else
    list = list.OrderByDescending(sorter);
}

Τώρα μπορείτε να καθορίσετε το πεδίο για ταξινόμηση όταν καλείτε τη μέθοδο Sort.

Sort(ref employees, e => e.DOB, SortDirection.Descending);
Σχόλια (7)

Θα μπορούσατε να χρησιμοποιήσετε την Reflection για να λάβετε την τιμή της ιδιότητας.

list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
           .ToList();

Όπου ο TypeHelper έχει μια στατική μέθοδο όπως:

public static class TypeHelper
{
    public static object GetPropertyValue( object obj, string name )
    {
        return obj == null ? null : obj.GetType()
                                       .GetProperty( name )
                                       .GetValue( obj, null );
    }
}

Μπορεί επίσης να θέλετε να δείτε το Dynamic LINQ από τη βιβλιοθήκη VS2008 Samples library. Θα μπορούσατε να χρησιμοποιήσετε την επέκταση IEnumerable για να μετατρέψετε τη λίστα σε IQueryable και στη συνέχεια να χρησιμοποιήσετε την επέκταση Dynamic link OrderBy.

 list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );
Σχόλια (7)

Απάντηση για το 1.:

Θα πρέπει να είστε σε θέση να δημιουργήσετε χειροκίνητα ένα δέντρο εκφράσεων που μπορεί να περάσει στο OrderBy χρησιμοποιώντας το όνομα ως συμβολοσειρά. Ή θα μπορούσατε να χρησιμοποιήσετε την αντανάκλαση όπως προτείνεται σε μια άλλη απάντηση, η οποία μπορεί να είναι λιγότερο δαπανηρή.

Edit: Εδώ είναι ένα παράδειγμα εργασίας για την κατασκευή ενός δέντρου εκφράσεων με το χέρι. (Ταξινόμηση με βάση την X.Value, όταν γνωρίζουμε μόνο το όνομα "Value" της ιδιότητας). Θα μπορούσατε (θα έπρεπε) να φτιάξετε μια γενική μέθοδο για να το κάνετε αυτό.

using System;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    private static readonly Random rand = new Random();
    static void Main(string[] args)
    {
        var randX = from n in Enumerable.Range(0, 100)
                    select new X { Value = rand.Next(1000) };

        ParameterExpression pe = Expression.Parameter(typeof(X), "value");
        var expression = Expression.Property(pe, "Value");
        var exp = Expression.Lambda(expression, pe).Compile();

        foreach (var n in randX.OrderBy(exp))
            Console.WriteLine(n.Value);
    }

    public class X
    {
        public int Value { get; set; }
    }
}

Ωστόσο, η κατασκευή ενός δέντρου εκφράσεων απαιτεί να γνωρίζετε τους συμμετέχοντες τύπους. Αυτό μπορεί να είναι ή να μην είναι πρόβλημα στο δικό σας σενάριο χρήσης. Αν δεν ξέρετε σε ποιον τύπο πρέπει να κάνετε ταξινόμηση, θα είναι πιθανώς ευκολότερο να χρησιμοποιήσετε την αντανάκλαση.

Απάντηση για το 2.:

Ναι, αφού το Comparer.Default θα χρησιμοποιηθεί για τη σύγκριση, αν δεν ορίσετε ρητά το comparer.

Σχόλια (1)