使用Lambda/Linq对列表对列表进行排序

Dot*_*ude 264 c# linq lambda linq-to-objects

我在字符串中有"按属性排序"的名称.我将需要使用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();    
  }

}
Run Code Online (Sandbox Code Playgroud)
  1. 而不是使用一堆ifs来检查fieldname(sortBy),是否有更简洁的方法进行排序
  2. 排序是否知道数据类型?

gls*_*123 352

这可以做到

list.Sort( (emp1,emp2)=>emp1.FirstName.CompareTo(emp2.FirstName) );
Run Code Online (Sandbox Code Playgroud)

.NET框架将lambda转换(emp1,emp2)=>intComparer<Employee>.

这具有强类型的优点.

  • 是的我不认为这样的事情?list.Sort((emp1,emp2)=> emp1.GetType().GetProperty(sortBy).GetValue(emp1,null).CompareTo(emp2.​​GetType().GetProperty(sortBy).GetValue(emp2,null))) ; (4认同)
  • 仅仅因为它是一个函数引用,它不一定是单行的。你可以只写`list.sort(functionDeclaredElsewhere)` (3认同)
  • 如何反向排序? (2认同)
  • @JerryGoyal 交换参数... emp2.​​FirstName.CompareTo(emp1.FirstName) 等。 (2认同)

Sam*_*uel 74

你可以做的一件事就是改变,Sort以便更好地利用lambdas.

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);
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以在调用Sort方法时指定要排序的字段.

Sort(ref employees, e => e.DOB, SortDirection.Descending);
Run Code Online (Sandbox Code Playgroud)

  • 由于sort列是一个字符串,你仍然需要一个switch/if-else块来确定传​​递它的函数. (7认同)
  • 我认为这更有可能是因为它来自网页上的排序控件,它将排序列作为字符串参数传回.无论如何,这将是我的用例. (6认同)
  • 他在问题中说,"按财产排序"是一个字符串.我只是回答他的问题. (3认同)
  • @tvanfosson - 你说得对,我有一个自定义控件,它将订单和字段名称作为字符串 (2认同)

tva*_*son 55

您可以使用Reflection来获取属性的值.

list = list.OrderBy( x => TypeHelper.GetPropertyValue( x, sortBy ) )
           .ToList();
Run Code Online (Sandbox Code Playgroud)

TypeHelper有一个静态方法,如:

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

您可能还想查看VS2008 Samples库中的 Dynamic LINQ .您可以使用IEnumerable扩展将List强制转换为IQueryable,然后使用动态链接OrderBy扩展.

 list = list.AsQueryable().OrderBy( sortBy + " " + sortDirection );
Run Code Online (Sandbox Code Playgroud)


小智 20

这就是我解决问题的方法:

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();
}
Run Code Online (Sandbox Code Playgroud)


Ras*_*ack 15

可以在此处阅读通过表达式构建订单

链接中的页面无耻地被盗:

// 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();
Run Code Online (Sandbox Code Playgroud)


Dan*_*ner 8

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

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

笔记

  1. 为什么通过引用传递列表?
  2. 您应该使用枚举作为排序方向.
  3. 如果要传递一个lambda表达式,指定要排序的属性而不是属性名称作为字符串,则可以获得更清晰的解决方案.
  4. 在我的示例中,list == null将导致NullReferenceException,您应该捕获这种情况.


Ser*_*uei 6

如果类型实现了,则使用IComparable接口.您可以通过实现自定义IComparer来避免ifs:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

然后

list.Sort(new EmpComp(sortBy));
Run Code Online (Sandbox Code Playgroud)


dri*_*iis 5

答案1:

您应该能够使用名称作为字符串手动构建可以传递到OrderBy的表达式树.或者您可以使用另一个答案中建议的反射,这可能会减少工作量.

编辑:以下是手动构建表达式树的工作示例.(在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<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; }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

回答2:

是的,因为如果没有明确定义比较器,将使用Comparer <T> .Default进行比较.


Ant*_*oin 5

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

为了让它适用于任何类型的属性,这是我找到的解决方案:

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;
    }
Run Code Online (Sandbox Code Playgroud)