Ich habe den Namen der "sort by property" in einer Zeichenfolge. Ich muss Lambda/Linq verwenden, um die Liste der Objekte zu sortieren.
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();
}
}
Dies kann so gemacht werden
list.Sort( (emp1,emp2)=>emp1.FirstName.CompareTo(emp2.FirstName) );
Das .NET Framework wirft das Lambda (emp1,emp2)=>int
Als ein Comparer<Employee>.
Dies hat den Vorteil einer starken Typisierung.
Eine Sache, die Sie tun können, ist, Sort
zu ändern, damit Lambdas besser genutzt werden.
public enum SortDirection { Ascending, Descending }
public void Sort<TKey>(ref List<Employee> list,
Func<Employee, TKey> sorter, SortDirection direction)
{
if (direction == SortDirection.Ascending)
list = list.OrderBy(sorter);
else
list = list.OrderByDescending(sorter);
}
Jetzt können Sie das Feld angeben, nach dem sortiert werden soll, wenn die Sort
-Methode aufgerufen wird.
Sort(ref employees, e => e.DOB, SortDirection.Descending);
Sie können Reflection verwenden, um den Wert der Eigenschaft abzurufen.
list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
.ToList();
Wobei TypeHelper eine statische Methode hat wie:
public static class TypeHelper
{
public static object GetPropertyValue( object obj, string name )
{
return obj == null ? null : obj.GetType()
.GetProperty( name )
.GetValue( obj, null );
}
}
Möglicherweise möchten Sie sich auch Dynamic LINQ aus der VS2008-Beispielbibliothek ansehen. Sie können die IEnumerable-Erweiterung verwenden, um die Liste als IQueryable umzuwandeln, und dann die Dynamic Link OrderBy-Erweiterung verwenden.
list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );
So habe ich mein Problem gelöst:
List<User> list = GetAllUsers(); //Private Method
if (!sortAscending)
{
list = list
.OrderBy(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
.ToList();
}
else
{
list = list
.OrderByDescending(r => r.GetType().GetProperty(sortBy).GetValue(r,null))
.ToList();
}
Das Bilden der Reihenfolge durch Ausdruck kann gelesen werden hier
Schamlos von der Seite im Link gestohlen:
// First we define the parameter that we are going to use
// in our OrderBy clause. This is the same as "(person =>"
// in the example above.
var param = Expression.Parameter(typeof(Person), "person");
// Now we'll make our lambda function that returns the
// "DateOfBirth" property by it's name.
var mySortExpression = Expression.Lambda<Func<Person, object>>(Expression.Property(param, "DateOfBirth"), param);
// Now I can sort my people list.
Person[] sortedPeople = people.OrderBy(mySortExpression).ToArray();
Sie könnten Reflektion verwenden, um auf die Eigenschaft zuzugreifen.
public List<Employee> Sort(List<Employee> list, String sortBy, String sortDirection)
{
PropertyInfo property = list.GetType().GetGenericArguments()[0].
GetType().GetProperty(sortBy);
if (sortDirection == "ASC")
{
return list.OrderBy(e => property.GetValue(e, null));
}
if (sortDirection == "DESC")
{
return list.OrderByDescending(e => property.GetValue(e, null));
}
else
{
throw new ArgumentOutOfRangeException();
}
}
Notizen
Sort verwendet die IComparable-Schnittstelle, wenn der Typ sie implementiert. Und Sie können das Wenn vermeiden, indem Sie einen benutzerdefinierten IComparer implementieren:
class EmpComp : IComparer<Employee>
{
string fieldName;
public EmpComp(string fieldName)
{
this.fieldName = fieldName;
}
public int Compare(Employee x, Employee y)
{
// compare x.fieldName and y.fieldName
}
}
und dann
list.Sort(new EmpComp(sortBy));
Antwort für 1.:
Sie sollten in der Lage sein, manuell einen Ausdrucksbaum zu erstellen, der unter Verwendung des Namens als Zeichenfolge an OrderBy übergeben werden kann. Oder Sie könnten Reflexion verwenden, wie in einer anderen Antwort vorgeschlagen, die möglicherweise weniger Arbeit ist.
Bearbeiten : Hier ist ein funktionierendes Beispiel für die manuelle Erstellung eines Ausdrucksbaums. (Sortierung nach X.Value, wenn nur der Name "Value" der Eigenschaft bekannt ist). Sie könnten (sollten) eine generische Methode dafür erstellen.
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<Func<X, int>>(expression, pe).Compile();
foreach (var n in randX.OrderBy(exp))
Console.WriteLine(n.Value);
}
public class X
{
public int Value { get; set; }
}
}
Um einen Ausdrucksbaum zu erstellen, müssen Sie jedoch die beteiligten Typen kennen. Dies könnte in Ihrem Nutzungsszenario ein Problem sein oder auch nicht. Wenn Sie nicht wissen, nach welchem Typ Sie sortieren sollen, ist die Verwendung der Reflexion wahrscheinlich einfacher.
Antwort für 2.:
Ja, da Comparer <T> .Default für den Vergleich verwendet wird, wenn Sie den Comparer nicht explizit definieren.
Die von Rashack bereitgestellte Lösung funktioniert leider nicht für Werttypen (int, enums, etc.).
Damit es mit jeder Art von Immobilie funktioniert, habe ich folgende Lösung gefunden:
public static Expression<Func<T, object>> GetLambdaExpressionFor<T>(this string sortColumn)
{
var type = typeof(T);
var parameterExpression = Expression.Parameter(type, "x");
var body = Expression.PropertyOrField(parameterExpression, sortColumn);
var convertedBody = Expression.MakeUnary(ExpressionType.Convert, body, typeof(object));
var expression = Expression.Lambda<Func<T, object>>(convertedBody, new[] { parameterExpression });
return expression;
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;
public static class EnumerableHelper
{
static MethodInfo orderBy = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderBy" && x.GetParameters().Length == 2).First();
public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, string propertyName)
{
var pi = typeof(TSource).GetProperty(propertyName, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
var selectorParam = Expression.Parameter(typeof(TSource), "keySelector");
var sourceParam = Expression.Parameter(typeof(IEnumerable<TSource>), "source");
return
Expression.Lambda<Func<IEnumerable<TSource>, IOrderedEnumerable<TSource>>>
(
Expression.Call
(
orderBy.MakeGenericMethod(typeof(TSource), pi.PropertyType),
sourceParam,
Expression.Lambda
(
typeof(Func<,>).MakeGenericType(typeof(TSource), pi.PropertyType),
Expression.Property(selectorParam, pi),
selectorParam
)
),
sourceParam
)
.Compile()(source);
}
public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, string propertyName, bool ascending)
{
return ascending ? source.OrderBy(propertyName) : source.OrderBy(propertyName).Reverse();
}
}
Eine andere, diesmal für jeden IQueryable:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class IQueryableHelper
{
static MethodInfo orderBy = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderBy" && x.GetParameters().Length == 2).First();
static MethodInfo orderByDescending = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(x => x.Name == "OrderByDescending" && x.GetParameters().Length == 2).First();
public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, params string[] sortDescriptors)
{
return sortDescriptors.Length > 0 ? source.OrderBy(sortDescriptors, 0) : source;
}
static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string[] sortDescriptors, int index)
{
if (index < sortDescriptors.Length - 1) source = source.OrderBy(sortDescriptors, index + 1);
string[] splitted = sortDescriptors[index].Split(' ');
var pi = typeof(TSource).GetProperty(splitted[0], BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.IgnoreCase);
var selectorParam = Expression.Parameter(typeof(TSource), "keySelector");
return source.Provider.CreateQuery<TSource>(Expression.Call((splitted.Length > 1 && string.Compare(splitted[1], "desc", StringComparison.Ordinal) == 0 ? orderByDescending : orderBy).MakeGenericMethod(typeof(TSource), pi.PropertyType), source.Expression, Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(TSource), pi.PropertyType), Expression.Property(selectorParam, pi), selectorParam)));
}
}
Sie können mehrere Sortierkriterien wie folgt übergeben:
var q = dc.Felhasznalos.OrderBy(new string[] { "Email", "FelhasznaloID desc" });