Trier une liste en utilisant Lambda/Linq aux objets

J'ai le nom de la propriété "trier par" dans une chaîne. J'aurai besoin d'utiliser Lambda/Linq pour trier la liste d'objets.

Ex :

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. Au lieu d'utiliser un tas de ifs pour vérifier le nom du champ (sortBy), existe-t-il un moyen plus propre de faire le tri ?
  2. Est-ce que sort est conscient du type de données ?

Une chose que vous pourriez faire est de modifier Sort pour qu'il fasse un meilleur usage des 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);
}

Maintenant vous pouvez spécifier le champ à trier en appelant la méthode Sort.

Sort(ref employees, e => e.DOB, SortDirection.Descending);
Commentaires (7)

Vous pouvez utiliser Reflection pour obtenir la valeur de la propriété.

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

Où TypeHelper a une méthode statique comme :

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

Vous pouvez également consulter Dynamic LINQ dans la [bibliothèque VS2008 Samples][1]. Vous pourriez utiliser l'extension IEnumerable pour transformer la liste en IQueryable, puis utiliser l'extension Dynamic Link OrderBy.

 list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );

[1] : http://msdn.microsoft.com/en-us/bb330936.aspx

Commentaires (7)

Réponse pour 1.:

Vous devriez être en mesure de construire manuellement un arbre d'expression qui peut être transmis à OrderBy en utilisant le nom comme chaîne. Vous pouvez également utiliser la réflexion comme suggéré dans une autre réponse, ce qui représente moins de travail.

Edit : Voici un exemple fonctionnel de construction manuelle d'un arbre d'expression. (Tri sur X.Value, en ne connaissant que le nom "Value&quot ; de la propriété). Vous pourriez (devriez) construire une méthode générique pour le faire.

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; }
    }
}

Pour construire un arbre d'expression, il faut cependant connaître les types correspondants. Cela peut ou non être un problème dans votre scénario d'utilisation. Si vous ne savez pas sur quel type vous devez faire le tri, il sera probablement plus facile d'utiliser la réflexion.

Réponse pour 2.:

Oui, puisque Comparer.Default sera utilisé pour la comparaison, si vous ne définissez pas explicitement le comparateur.

Commentaires (1)