【问题标题】:linq Order By for a List(Of myObjects)linq 为列表排序(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 是一个传递的字符串值

【问题讨论】:

    标签: .net asp.net vb.net linq


    【解决方案1】:

    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 的解决方案。
    【解决方案2】:

    将排序列作为函数传递。

    原来如此

    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 委托不返回值。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      如果你只是传递了一个字符串,你就不能(容易地)做到这一点。您可以有一个从 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 传入(可能通过使您的方法通用),这将避免这里的混乱。

      【讨论】:

        【解决方案5】:

        您可以使用 .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()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-13
          相关资源
          最近更新 更多