linq 为列表排序(MyObjects)

Posted

技术标签:

【中文标题】linq 为列表排序(MyObjects)【英文标题】:linq Order By for a List(Of myObjects) 【发布时间】:2010-10-11 01:21:33 【问题描述】:

如何在我的对象列表中按传递的字符串值排序?我需要对我的 List(Of) 对象进行分页和排序,分页没问题,但我不知道该让谁来工作。

这是我目前正在做的事情,效果很好:

Return returnReports.Skip(PageSize * (PageNumber-1)).Take(PageSize).ToList()

如何让它工作?

Return returnReports.OrderBy(SortColumn).Skip(skip).Take(PageSize).ToList()

SortColumn 是一个传递的字符串值

【问题讨论】:

【参考方案1】:

您可以使用 .Sort 代替 .OrderBy。只需为您的对象创建一个 Comparer 类,例如 this。

public class FooComparer : IComparer<Foo>
   
      private readonly string _sortBy;
      private readonly bool _sortDesc;

      public FooComparer(string sortBy)
      
         _sortBy = sortBy.ToLower();
         _sortDesc = _sortBy.EndsWith(" desc");
         _sortBy = _sortBy.Replace(" asc", string.Empty).Replace(" desc", string.Empty);
      

      //implement IComparer method
      public int Compare(Foo x, Foo y)
      
         int ret = 0;

         switch (_sortBy)
         
            //must match lowercase sortname 
            case "date":
               ret = DateTime.Compare(x.SomeDate, y.SomeDate);
               break;
            //other properties you can sort by as above...

         

         //if there is a tie, break it consistently - this will be specific to your object
         if (ret == 0)
         
            ret = (DateTime.Compare(x.InsertDate, y.InsertDate));
         

         if (_sortDesc)
         
            ret *= -1;
         

         return ret;
      
   

那么排序调用就变成了:

Return Foo.Sort(new FooComparer(sortName + " " + sortOrder)

所以,你的会变成(排序顺序在未指定时默认为升序):

returnReports.Sort(new ReturnReportsComparer(SortColumn));

return returnReports.Skip(skip).Take(PageSize).ToList()

【讨论】:

【参考方案2】:

VB

Module OrderByExtensions
  <System.Runtime.CompilerServices.Extension()> _
  Public Function OrderByPropertyName(Of T)(ByVal e As IEnumerable(Of T), ByVal propertyName As String) As IOrderedEnumerable(Of T)
    Dim itemType = GetType(T)
    Dim prop = itemType.GetProperty(propertyName)
    If prop Is Nothing Then Throw New ArgumentException("Object does not have the specified property")
    Dim propType = prop.PropertyType
    Dim funcType = GetType(Func(Of ,)).MakeGenericType(itemType, propType)
    Dim parameter = Expression.Parameter(itemType, "item")
    Dim exp = Expression.Lambda(funcType, Expression.MakeMemberAccess(parameter, prop), parameter)
    Dim params = New Object() e, exp.Compile()
    Return DirectCast(GetType(OrderByExtensions).GetMethod("InvokeOrderBy", Reflection.BindingFlags.Static Or Reflection.BindingFlags.NonPublic).MakeGenericMethod(itemType, propType).Invoke(Nothing, params), IOrderedEnumerable(Of T))
  End Function
  Private Function InvokeOrderBy(Of T, U)(ByVal e As IEnumerable(Of T), ByVal f As Func(Of T, U)) As IOrderedEnumerable(Of T)
    Return Enumerable.OrderBy(e, f)
  End Function
End Module

C#

public static class OrderByExtensions

  public static IOrderedEnumerable<T> OrderByPropertyName<T>(this IEnumerable<T> e, string name)
  
    var itemType = typeof(T);
    var prop = itemType.GetProperty(name);
    if (prop == null) throw new ArgumentException("Object does not have the specified property");
    var propType = prop.PropertyType;
    var funcType = typeof(Func<,>).MakeGenericType(itemType, propType);
    var parameter = Expression.Parameter(itemType, "item");
    var memberAccess = Expression.MakeMemberAccess(parameter, prop);
    var expression = Expression.Lambda(funcType, memberAccess, parameter);
    var x = typeof(OrderByExtensions).GetMethod("InvokeOrderBy", BindingFlags.Static | BindingFlags.NonPublic);
    return (IOrderedEnumerable<T>)x.MakeGenericMethod(itemType, propType).Invoke(null, new object[]  e, expression.Compile() );
  
  static IOrderedEnumerable<T> InvokeOrderBy<T, U>(IEnumerable<T> e, Func<T, U> f)
  
    return e.OrderBy(f);
  

【讨论】:

不过,当 SortColumn 作为字符串传入时,这是行不通的。 你已经错过了 C# 代码中 e 参数中的 this,因为它们不是扩展方法。 @Richard:这很有趣,因为我一直在努力确保 VB 是正确的(作为一个 C# 人)我在翻译成 C# 时错过了this 这太棒了——我得研究一下。还有一件事,我将如何做到这一点,以便我可以说是要按升序还是降序排列? 只需复制并粘贴它并替换所有 OrderBy -> OrderByDescending。顺便说一句,这不是一个非常高效的方法,因为它必须使用反射并动态构建表达式。如果您知道您正在使用的类型,请使用 Jon 的解决方案。【参考方案3】:

将排序列作为函数传递。

原来如此

public SomeList Foo(Function<Foo, bool> sortFunction, int skip, int PageSize)

   return returnReports.OrderBy(sortFunction).Skip(skip).Take(PageSize).ToList();

这样称呼

SomeList(f => f.Bar, 5, 10);

【讨论】:

Action 委托不返回值。【参考方案4】:

如果您使用数据库作为数据源,那么您可以使用Dynamic LINQ 项目,该项目允许您将 Where 子句的参数指定为字符串。

如果您使用“Linq to objects”,则需要创建作为参数动态传递的 lambda 函数。为此,您可以使用“Expression.Xyz”方法构建表达式树,然后使用“Compile”方法将表达式树转换为可调用委托(Func 类型),您可以将其用作参数在哪里。 another SO thread here 有一个如何构造表达式树的例子。

【讨论】:

【参考方案5】:

如果你只是传递了一个字符串,你就不能(容易地)做到这一点。您可以有一个从 String 到 Func&lt;IEnumerable&lt;Report&gt;, IEnumerable&lt;Report&gt;&gt; 的映射,例如(在 C# 中)

// Horrible type. Ick.
private static readonly
    Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>> 
    Orderings = 
    new Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>>

     "FirstColumn", (IEnumerable<Report> reports) => 
                          reports.OrderBy(report => report.FirstColumn) ,
     "SecondColumn", (IEnumerable<Report> reports) => 
                          reports.OrderBy(report => report.SecondColumn) ,

    (etc)
;

然后使用:

// For production usage, include some error checking!
return Orderings[sortColumn].Skip(skip).Take(pageSize).ToList();

如果您可以将SortColumn 作为适当的Func 传入(可能通过使您的方法通用),这将避免这里的混乱。

【讨论】:

以上是关于linq 为列表排序(MyObjects)的主要内容,如果未能解决你的问题,请参考以下文章

如何简化在字符串中搜索关键字并按相关性排序的 LINQ 查询?

使用 Linq 按名称频率对列表进行排序

使用 LINQ 排序后获取集合中项目的新索引

排序列表,除了一个带有 LINQ 的条目,排在最后

使用 LINQ 将字符串转换为 int 以进行排序

在 C# 中使用 LINQ 按范围排序列表