Razvrščanje seznama z uporabo Lambda/Linq na predmete

Imam ime lastnosti "sort by" v nizu. Za razvrščanje seznama predmetov bom moral uporabiti Lambda/Linq.

Npr:

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. Ali obstaja čistejši način razvrščanja, namesto da za preverjanje imena polja (sortBy) uporabljamo več ifov?
  2. Ali se sortiranje zaveda podatkovnega tipa?

Ena stvar, ki bi jo lahko naredili, je, da spremenite Sort tako, da bo bolje uporabljal lambde.

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

Zdaj lahko pri klicu metode Sort določite polje za razvrščanje.

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

Za pridobitev vrednosti lastnosti lahko uporabite funkcijo Refleksija.

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

Če ima TypeHelper statično metodo, kot je:

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

Morda si boste želeli ogledati tudi Dynamic LINQ iz knjižnice VS2008 Samples. Uporabite lahko razširitev IEnumerable, da seznam pretvorite v IQueryable in nato uporabite razširitev Dynamic link OrderBy.

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

Odgovor za 1. točko:

Morali bi biti sposobni ročno sestaviti drevo izrazov, ki ga lahko posredujete v ukaz OrderBy z uporabo imena kot niza. Lahko pa uporabite refleksijo, kot je predlagano v drugem odgovoru, kar bi bilo morda manj naporno.

Upravi: Tukaj je delujoč primer ročne izgradnje izraznega drevesa. (Razvrščanje po X.Value, če poznamo samo ime "Value" lastnosti). Za to bi lahko (morali) izdelati splošno metodo.

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

Vendar pa je za izgradnjo izraznega drevesa treba poznati posamezne vrste. To je lahko težava v vašem scenariju uporabe, lahko pa tudi ne. Če ne veste, po katerem tipu bi morali razvrščati, bo verjetno lažje uporabiti refleksijo.

Odgovor za 2. točko:

Da, saj bo za primerjavo uporabljen Comparer.Default, če ne boste izrecno določili primerjalnika.

Komentarji (1)