【问题标题】:How to Sort a List<T> by a property in the object如何按对象中的属性对 List<T> 进行排序
【发布时间】:2020-08-03 20:08:46
【问题描述】:

我有一个名为Order 的类,它具有OrderIdOrderDateQuantityTotal 等属性。我有这个Order 类的列表:

List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders

现在我想根据Order 对象的一个​​属性对列表进行排序,例如我需要按订单日期或订单ID 对其进行排序。

如何在 C# 中做到这一点?

【问题讨论】:

    标签: c# generics list sorting


    【解决方案1】:

    任何使用可空类型的人,Value 都必须使用CompareTo

    objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
    

    【讨论】:

      【解决方案2】:
      • 如果需要对 Question 实体中的字符串 Id 进行排序

      • 解析Id后使用Sort函数和delegate对Id进行排序 价值

          class Question
          {
              public List<QuestionInfo> Questions Info{ get; set; }
          
          }
      
          class QuestionInfo
          {
              public string Id{ get; set; }
              public string Questions{ get; set; }
          
          }
      
          var questionnaire = new Question();
           questionnaire.QuestionInfo.Sort((x, y) => int.Parse(x.Id, CultureInfo.CurrentCulture) - int.Parse(y.Id, CultureInfo.CurrentCulture));
      
      

      【讨论】:

        【解决方案3】:

        您好,只是回到这个问题。 如果要对这个序列“1”“10”“100”“200”“2”“20”“3”“30”“300”的List进行排序,得到1;2;3这种形式的排序项;10;20;30;100;200;300 你可以使用这个:

         public class OrderingAscending : IComparer<String>
            {
                public int Compare(String x, String y)
                {
                    Int32.TryParse(x, out var xtmp);
                    Int32.TryParse(y, out var ytmp);
        
                    int comparedItem = xtmp.CompareTo(ytmp);
                    return comparedItem;
                }
            }
        

        你可以在后面的代码中以这种形式使用它:

         IComparer<String> comparerHandle = new OrderingAscending();
         yourList.Sort(comparerHandle);
        

        【讨论】:

          【解决方案4】:

          假设您有以下代码,在此代码中,我们有一个带有几个属性的Passenger 类,我们希望根据这些属性进行排序。

          public class Passenger
          {
              public string Name { get; }
              public string LastName { get; }
              public string PassportNo { get; }
              public string Nationality { get; }
          
              public Passenger(string name, string lastName, string passportNo, string nationality)
              {
                  this.Name = name;
                  this.LastName = lastName;
                  this.PassportNo = passportNo;
                  this.Nationality = nationality;
              }
          
              public static int CompareByName(Passenger passenger1, Passenger passenger2)
              {
                  return String.Compare(passenger1.Name, passenger2.Name);
              }
          
              public static int CompareByLastName(Passenger passenger1, Passenger passenger2)
              {
                  return String.Compare(passenger1.LastName, passenger2.LastName);
              }
          
              public static int CompareNationality(Passenger passenger1, Passenger passenger2)
              {
                  return String.Compare(passenger1.Nationality, passenger2.Nationality);
              }
          }
          
          public class TestPassengerSort
          {
              Passenger p1 = new Passenger("Johon", "Floid", "A123456789", "USA");
              Passenger p2 = new Passenger("Jo", "Sina", "A987463215", "UAE");
              Passenger p3 = new Passenger("Ped", "Zoola", "A987855215", "Italy");
          
              public void SortThem()
              {
                  Passenger[] passengers = new Passenger[] { p1, p2, p3 };
                  List<Passenger> passengerList = new List<Passenger> { p1, p2, p3 };
          
                  Array.Sort(passengers, Passenger.CompareByName);
                  Array.Sort(passengers, Passenger.CompareByLastName);
                  Array.Sort(passengers, Passenger.CompareNationality);
          
                  passengerList.Sort(Passenger.CompareByName);
                  passengerList.Sort(Passenger.CompareByLastName);
                  passengerList.Sort(Passenger.CompareNationality);
          
              }
          }
          

          因此您可以使用组合委托来实现您的排序结构。

          【讨论】:

            【解决方案5】:

            如果您需要对列表进行就地排序,则可以使用Sort 方法,传递Comparison&lt;T&gt; 委托:

            objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
            

            如果您更喜欢创建一个新的排序序列而不是就地排序,那么您可以使用 LINQ 的 OrderBy 方法,如其他答案中所述。

            【讨论】:

            • 是的,这是“正确”的答案,应该比创建新的 IEnumerable 然后将其转换为新列表更有效。
            • 当然,如果你需要降序排序,把箭头右边的xy交换一下=&gt;
            • 真正就地排序列表的真正答案
            • @PimBrouwers 这是更好的选择期。即使您使用的内存量不是问题,此解决方案也可以避免不必要的内存分配,这非常昂贵。此选项在代码方面同样简单,并且速度快了一个数量级。
            • @JonSchneider 对于Nullable&lt;&gt;(以DateTime? 为例),您可以使用.Sort((x, y) =&gt; Nullable.Compare(x.OrderDate, y.OrderDate)),它将null 视为所有非null 值的前面。和.Sort((x, y) =&gt; Comparer&lt;DateTime?&gt;.Default.Compare(x.OrderDate, y.OrderDate)一模一样。
            【解决方案6】:

            订购列表的最简单方法是使用OrderBy

             List<Order> objListOrder = 
                source.OrderBy(order => order.OrderDate).ToList();
            

            如果您想按多列排序,如以下 SQL 查询。

            ORDER BY OrderDate, OrderId
            

            要实现这一点,您可以使用ThenBy,如下所示。

              List<Order> objListOrder = 
                source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
            

            【讨论】:

              【解决方案7】:
              var obj = db.Items.Where...
              
              var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
              

              【讨论】:

                【解决方案8】:

                经典的面向对象解决方案

                首先,我必须为 LINQ 的威力而跪下……现在我们已经解决了这个问题

                JimmyHoffa 答案的变体。使用泛型,CompareTo 参数变得类型安全。

                public class Order : IComparable<Order> {
                
                    public int CompareTo( Order that ) {
                        if ( that == null ) return 1;
                        if ( this.OrderDate > that.OrderDate) return 1;
                        if ( this.OrderDate < that.OrderDate) return -1;
                        return 0;
                    }
                }
                
                // in the client code
                // assume myOrders is a populated List<Order>
                myOrders.Sort(); 
                

                这个默认的可排序性当然是可重复使用的。也就是说,每个客户端都不必冗余地重写排序逻辑。交换“1”和“-1”(或逻辑运算符,您的选择)会颠倒排序顺序。

                【讨论】:

                • 对列表中的对象进行排序的简单方法。但我不明白你为什么返回 1 if (that == null)?
                • 表示this 对象大于空。出于排序的目的,空对象引用“小于”this 对象。这正是我决定定义空值如何排序的方式。
                【解决方案9】:

                以上答案对我来说都不够通用,所以我做了这个:

                var someUserInputStringValue = "propertyNameOfObject i.e. 'Quantity' or 'Date'";
                var SortedData = DataToBeSorted
                                   .OrderBy(m => m.GetType()
                                                  .GetProperties()
                                                  .First(n => 
                                                      n.Name == someUserInputStringValue)
                                   .GetValue(m, null))
                                 .ToList();
                

                但要小心处理海量数据集。这是简单的代码,但如果集合很大并且集合的对象类型具有大量字段,则可能会给您带来麻烦。 运行时间为 NxM,其中:

                N = 集合中的元素数

                M = 对象中的属性数

                【讨论】:

                  【解决方案10】:

                  请让我用一些示例代码完成@LukeH 的答案,因为我已经对其进行了测试,我相信它可能对某些人有用:

                  public class Order
                  {
                      public string OrderId { get; set; }
                      public DateTime OrderDate { get; set; }
                      public int Quantity { get; set; }
                      public int Total { get; set; }
                  
                      public Order(string orderId, DateTime orderDate, int quantity, int total)
                      {
                          OrderId = orderId;
                          OrderDate = orderDate;
                          Quantity = quantity;
                          Total = total;
                      }
                  }
                  
                  public void SampleDataAndTest()
                  {
                      List<Order> objListOrder = new List<Order>();
                  
                      objListOrder.Add(new Order("tu me paulo ", Convert.ToDateTime("01/06/2016"), 1, 44));
                      objListOrder.Add(new Order("ante laudabas", Convert.ToDateTime("02/05/2016"), 2, 55));
                      objListOrder.Add(new Order("ad ordinem ", Convert.ToDateTime("03/04/2016"), 5, 66));
                      objListOrder.Add(new Order("collocationem ", Convert.ToDateTime("04/03/2016"), 9, 77));
                      objListOrder.Add(new Order("que rerum ac ", Convert.ToDateTime("05/02/2016"), 10, 65));
                      objListOrder.Add(new Order("locorum ; cuius", Convert.ToDateTime("06/01/2016"), 1, 343));
                  
                  
                      Console.WriteLine("Sort the list by date ascending:");
                      objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
                  
                      foreach (Order o in objListOrder)
                          Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
                  
                      Console.WriteLine("Sort the list by date descending:");
                      objListOrder.Sort((x, y) => y.OrderDate.CompareTo(x.OrderDate));
                      foreach (Order o in objListOrder)
                          Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
                  
                      Console.WriteLine("Sort the list by OrderId ascending:");
                      objListOrder.Sort((x, y) => x.OrderId.CompareTo(y.OrderId));
                      foreach (Order o in objListOrder)
                          Console.WriteLine("OrderId = " + o.OrderId + " OrderDate = " + o.OrderDate.ToString() + " Quantity = " + o.Quantity + " Total = " + o.Total);
                  
                      //etc ...
                  }
                  

                  【讨论】:

                    【解决方案11】:

                    您可以对属性选择做一些更通用的事情,但具体说明您选择的类型,在您的情况下是“订单”:

                    把你的函数写成一个通用的:

                    public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
                            {
                                return (from order in orders
                                        orderby propertySelector(order)
                                        select order).ToList();
                            } 
                    

                    然后像这样使用它:

                    var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);
                    

                    您可以更加通用,并为您想要订购的产品定义一个开放类型:

                    public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
                            {
                                return (from item in collection
                                        orderby propertySelector(item)
                                        select item).ToList();
                            } 
                    

                    并以同样的方式使用它:

                    var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);
                    

                    这是一种愚蠢的不必要的复杂方式来执行 LINQ 样式“OrderBy”, 但它可能会为您提供如何以通用方式实现它的线索

                    【讨论】:

                      【解决方案12】:

                      我能想到的最简单的方法是使用 Linq:

                      List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
                      

                      【讨论】:

                      • 如何按降序排序。
                      • @BonusKun List SortedList = objListOrder。 OrderByDescending (o=>o.OrderDate).ToList();
                      • 请注意,这会创建一个包含内存中所有项目的全新列表,这可能在性能方面存在问题。
                      • @staafl 将 listWithObjects = listWithObjects.OrderByDescending(o =&gt; o.Status).ToList(); 满足这样的努力吗?
                      • @staafl 我们正在订购对象引用列表,而不是据我所知复制对象本身。虽然这确实使引用列表使用的内存增加了一倍,但它并不像实际复制所有对象本身那么糟糕,所以在大多数情况下,除了我们处理大量数据集并将它们保存在内存中已经是一个问题,那么应该够了。
                      【解决方案13】:

                      基于 GenericTypeTea 的比较器:
                      我们可以通过添加排序标志来获得更大的灵活性:

                      public class MyOrderingClass : IComparer<Order> {  
                          public int Compare(Order x, Order y) {  
                              int compareDate = x.Date.CompareTo(y.Date);  
                              if (compareDate == 0) {  
                                  int compareOrderId = x.OrderID.CompareTo(y.OrderID);  
                      
                                  if (OrderIdDescending) {  
                                      compareOrderId = -compareOrderId;  
                                  }  
                                  return compareOrderId;  
                              }  
                      
                              if (DateDescending) {  
                                  compareDate = -compareDate;  
                              }  
                              return compareDate;  
                          }  
                      
                          public bool DateDescending { get; set; }  
                          public bool OrderIdDescending { get; set; }  
                      }  
                      

                      在这种情况下,您必须将其显式实例化为 MyOrderingClass(而不是 IComparer
                      为了设置它的排序属性:

                      MyOrderingClass comparer = new MyOrderingClass();  
                      comparer.DateDescending = ...;  
                      comparer.OrderIdDescending = ...;  
                      orderList.Sort(comparer);  
                      

                      【讨论】:

                        【解决方案14】:

                        从性能的角度来看,最好的方法是使用排序列表,以便数据在添加到结果时进行排序。 其他方法至少需要对数据进行一次额外的迭代,并且大多数方法会创建数据副本,因此不仅性能而且内存使用也会受到影响。对于数百个元素可能不是问题,但对于数千个元素来说可能不是问题,尤其是在许多并发请求可能同时进行排序的服务中。 查看 System.Collections.Generic 命名空间并选择一个具有排序功能的类而不是 List。

                        并尽可能避免使用反射的通用实现,这也会导致性能问题。

                        【讨论】:

                        • 如何提高性能?将数据插入排序列表是 O(n),因此添加 n 项是 O(n^2)。许多并发项目试图添加什么?在某些情况下,您在添加项目时需要消耗额外的 CPU 周期,但这不是一个好的通用解决方案。
                        【解决方案15】:

                        这是一个通用的 LINQ 扩展方法,它不会创建列表的额外副本:

                        public static void Sort<T,U>(this List<T> list, Func<T, U> expression)
                            where U : IComparable<U>
                        {
                            list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y)));
                        }
                        

                        使用它:

                        myList.Sort(x=> x.myProperty);
                        

                        我最近构建了这个额外的接受ICompare&lt;U&gt;,以便您可以自定义比较。当我需要进行自然字符串排序时,这会派上用场:

                        public static void Sort<T, U>(this List<T> list, Func<T, U> expression, IComparer<U> comparer)
                            where U : IComparable<U>
                        {    
                            list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y)));
                        }
                        

                        【讨论】:

                        • 我已经实现了这个,工作正常。我添加了一个“isAscending = true”参数。要按降序排序,只需在两个 Invoke() 方法中交换 x 和 y。谢谢。
                        • 如果您只是要编译表达式,您不妨先接受一个委托。此外,如果选择器导致副作用、计算成本高或不确定,则此方法存在重大问题。基于所选表达式的正确排序需要对列表中的每个项目调用选择器不超过一次。
                        • @Servy - 这些都是有效的观点,但老实说,我不确定如何实施您的一些建议(尤其是阻止选择器引起副作用......?)。我已将它们更改为代表。如果您知道如何进行其中一些更改,我很乐意让您编辑我的代码。
                        • @Peter You 不能防止代表造成副作用。您可以做的是确保每个对象不会多次调用它们,这意味着计算每个对象的值,存储对象/投影值对,然后对 that 进行排序。 OrderBy 在内部完成所有这些工作。
                        • 本着这种精神编写void 扩展方法的另一种好方法:static class Extensions { public static void SortBy&lt;TSource, TKey&gt;(this List&lt;TSource&gt; self, Func&lt;TSource, TKey&gt; keySelector) { self.SortBy(keySelector, Comparer&lt;TKey&gt;.Default); } public static void SortBy&lt;TSource, TKey&gt;(this List&lt;TSource&gt; self, Func&lt;TSource, TKey&gt; keySelector, IComparer&lt;TKey&gt; comparer) { self.Sort((x, y) =&gt; comparer.Compare(keySelector(x), keySelector(y))); } } 可以由SortByDescending 方法补充。 @Servy 的 cmets 也适用于我的代码!
                        【解决方案16】:

                        如你所说,不使用 Linq:

                        public class Order : IComparable
                        {
                            public DateTime OrderDate { get; set; }
                            public int OrderId { get; set; }
                        
                            public int CompareTo(object obj)
                            {
                                Order orderToCompare = obj as Order;
                                if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
                                {
                                    return 1;
                                }
                                if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
                                {
                                    return -1;
                                }
                        
                                // The orders are equivalent.
                                return 0;
                            }
                        }
                        

                        然后在您的订单列表中调用 .sort()

                        【讨论】:

                        • 必须首先在as 演员表上测试null。这是as 的重点,因为 (ha, ha) (Order)obj 在失败时会引发异常。 if(orderToCompare == null) return 1;.
                        • +1 用于使用接口,这使得代码更易于维护,并且清楚地暴露了对象的功能。
                        • 如果 Bill 和 Ted 出现,我会要求他们带我回到 2014 年 10 月 16 日,这样我就可以更正上面的错误 - 如果演员表失败,as 将返回 null。但至少空测试是正确的。
                        • @radarbob 是的......哎呀。 :) 但!此函数旨在通过对 List&lt;Order&gt; 列表的排序自动使用,因此应保证类型与 as 匹配,因此 2014 年您可能没有编写错误,就像避免不必要的警卫一样声明:)
                        • 应该保证 有趣的一点。如果它被很好地封装以至于它不能被调用,除非传递一个List&lt;Order&gt;;但是你和我都遇到过自我封装的程序员,他不言而喻的假设是“我正在编写这段代码,所以它不会被错误使用”
                        【解决方案17】:

                        Roger 版本的改进版。

                        GetDynamicSortProperty 的问题是只能获取属性名称,但是如果在 GridView 中我们使用 NavigationProperties 会发生什么?它将发送一个异常,因为它发现 null。

                        例子:

                        “Employee.Company.Name;”会崩溃...因为只允许“Name”作为参数来获取它的值。

                        这是一个改进的版本,允许我们按导航属性进行排序。

                        public object GetDynamicSortProperty(object item, string propName)
                            {
                                try
                                {                 
                                    string[] prop = propName.Split('.'); 
                        
                                    //Use reflection to get order type                   
                                    int i = 0;                    
                                    while (i < prop.Count())
                                    {
                                        item = item.GetType().GetProperty(prop[i]).GetValue(item, null);
                                        i++;
                                    }                     
                        
                                    return item;
                                }
                                catch (Exception ex)
                                {
                                    throw ex;
                                }
                        
                        
                            } 
                        

                        【讨论】:

                          【解决方案18】:

                          // 用于网格视图的完全通用排序

                          public List<T> Sort_List<T>(string sortDirection, string sortExpression, List<T> data)
                              {
                          
                                  List<T> data_sorted = new List<T>();
                          
                                  if (sortDirection == "Ascending")
                                  {
                                      data_sorted = (from n in data
                                                        orderby GetDynamicSortProperty(n, sortExpression) ascending
                                                        select n).ToList();
                                  }
                                  else if (sortDirection == "Descending")
                                  {
                                      data_sorted = (from n in data
                                                        orderby GetDynamicSortProperty(n, sortExpression) descending
                                                        select n).ToList();
                          
                                  }
                          
                                  return data_sorted;
                          
                              }
                          
                              public object GetDynamicSortProperty(object item, string propName)
                              {
                                  //Use reflection to get order type
                                  return item.GetType().GetProperty(propName).GetValue(item, null);
                              }
                          

                          【讨论】:

                          • 或者,你知道,data.OrderBy()。比重新发明轮子更容易。
                          • 这个想法是适用于任何类型的对象。其中 OrderBy() 仅适用于强类型对象。
                          • 非常感谢。在我看来,最简单、最实用的网格数据排序解决方案!
                          【解决方案19】:
                          //Get data from database, then sort list by staff name:
                          
                          List<StaffMember> staffList = staffHandler.GetStaffMembers();
                          
                          var sortedList = from staffmember in staffList
                                           orderby staffmember.Name ascending
                                           select staffmember;
                          

                          【讨论】:

                            【解决方案20】:

                            要在 .Net2.0 上不使用 LINQ:

                            List<Order> objListOrder = GetOrderList();
                            objListOrder.Sort(
                                delegate(Order p1, Order p2)
                                {
                                    return p1.OrderDate.CompareTo(p2.OrderDate);
                                }
                            );
                            

                            如果您使用的是 .Net3.0,那么 LukeH 的 answer 就是您所追求的。

                            要对多个属性进行排序,您仍然可以在委托中进行。例如:

                            orderList.Sort(
                                delegate(Order p1, Order p2)
                                {
                                    int compareDate = p1.Date.CompareTo(p2.Date);
                                    if (compareDate == 0)
                                    {
                                        return p2.OrderID.CompareTo(p1.OrderID);
                                    }
                                    return compareDate;
                                }
                            );
                            

                            这会给你升序日期和降序 orderIds。

                            但是,我不建议坚持使用委托,因为这意味着很多地方没有代码重用。您应该实现一个 IComparer 并将其传递给您的 Sort 方法。见here

                            public class MyOrderingClass : IComparer<Order>
                            {
                                public int Compare(Order x, Order y)
                                {
                                    int compareDate = x.Date.CompareTo(y.Date);
                                    if (compareDate == 0)
                                    {
                                        return x.OrderID.CompareTo(y.OrderID);
                                    }
                                    return compareDate;
                                }
                            }
                            

                            然后要使用这个 IComparer 类,只需将其实例化并将其传递给您的 Sort 方法:

                            IComparer<Order> comparer = new MyOrderingClass();
                            orderList.Sort(comparer);
                            

                            【讨论】:

                            • 优秀的答案,应该是正确的答案,因为它可以保护重新初始化原始列表(LINQ 版本将始终这样做)提供更好的封装。
                            • @wonton,没有。这个想法是能够有不同的IComparer 实现,给我们多态行为。
                            • 为我工作。我发布了一个带有排序选项的版本。
                            【解决方案21】:

                            利用 LiNQ OrderBy

                            List<Order> objListOrder=new List<Order> ();
                                objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
                            

                            【讨论】:

                              【解决方案22】:

                              使用 LINQ

                              objListOrder = GetOrderList()
                                                 .OrderBy(o => o.OrderDate)
                                                 .ToList();
                              
                              objListOrder = GetOrderList()
                                                 .OrderBy(o => o.OrderId)
                                                 .ToList();
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2023-03-27
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2019-07-02
                                • 1970-01-01
                                相关资源
                                最近更新 更多