我在字符串中有“按属性排序”的名称。我需要使用Lambda/Linq对对象列表进行排序。

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

}

与其使用一堆if来检查字段名(sortBy),还有一种更干净的排序方式吗 排序是否知道数据类型?


当前回答

通过表达式构建顺序可以在这里阅读

无耻地窃取页面链接:

// 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();

其他回答

回答1。

您应该能够手动构建一个表达式树,该表达式树可以使用名称作为字符串传递到OrderBy。或者你可以像另一个答案中建议的那样使用反思,这可能会更省事。

编辑:这是一个手动构建表达式树的工作示例。(当只知道属性的名称“Value”时,对X.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<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; }
    }
}

然而,构建表达式树要求您了解参与的类型。在您的使用场景中,这可能是问题,也可能不是问题。如果您不知道应该对哪种类型进行排序,那么使用反射可能会更容易。

答案是2。

是的,因为Comparer<T>。如果没有显式定义比较器,则将使用Default进行比较。

这可以用

list.Sort( (emp1,emp2)=>emp1.FirstName.CompareTo(emp2.FirstName) );

. net框架将lambda (emp1,emp2)=>int转换为比较器<Employee>。

这具有强类型的优点。

如果需要降序/倒序,则反转参数。

list.Sort( (emp1,emp2)=>emp2.FirstName.CompareTo(emp1.FirstName) );

您可以使用反射来访问该属性。

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

笔记

你为什么要传阅这份名单? 您应该使用枚举作为排序方向。 如果传递一个lambda表达式,可以得到一个更简洁的解决方案 指定要排序的属性,而不是以字符串形式指定属性名。 在我的例子中,list == null将导致NullReferenceException,您应该捕获这种情况。

如果你得到排序列名和排序方向为字符串,并且不想使用switch或If \else语法来确定列,那么这个例子可能对你很有趣:

private readonly Dictionary<string, Expression<Func<IuInternetUsers, object>>> _sortColumns = 
        new Dictionary<string, Expression<Func<IuInternetUsers, object>>>()
    {
        { nameof(ContactSearchItem.Id),             c => c.Id },
        { nameof(ContactSearchItem.FirstName),      c => c.FirstName },
        { nameof(ContactSearchItem.LastName),       c => c.LastName },
        { nameof(ContactSearchItem.Organization),   c => c.Company.Company },
        { nameof(ContactSearchItem.CustomerCode),   c => c.Company.Code },
        { nameof(ContactSearchItem.Country),        c => c.CountryNavigation.Code },
        { nameof(ContactSearchItem.City),           c => c.City },
        { nameof(ContactSearchItem.ModifiedDate),   c => c.ModifiedDate },
    };

    private IQueryable<IuInternetUsers> SetUpSort(IQueryable<IuInternetUsers> contacts, string sort, string sortDir)
    {
        if (string.IsNullOrEmpty(sort))
        {
            sort = nameof(ContactSearchItem.Id);
        }

        _sortColumns.TryGetValue(sort, out var sortColumn);
        if (sortColumn == null)
        {
            sortColumn = c => c.Id;
        }

        if (string.IsNullOrEmpty(sortDir) || sortDir == SortDirections.AscendingSort)
        {
            contacts = contacts.OrderBy(sortColumn);
        }
        else
        {
            contacts = contacts.OrderByDescending(sortColumn);
        }

        return contacts;
    }

基于使用Dictionary的解决方案,通过表达式>和它的键字符串连接排序列所需的字典。

不幸的是,Rashack提供的解决方案不适用于值类型(int、enum等)。

对于任何类型的属性,这是我发现的解决方案:

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