How to Sort a List<T> by a property in the object
我有一个名为
1 2 |
现在,我想基于
我怎么能用C来做这个?
我能想到的最简单的方法是使用LINQ:
1 | List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList(); |
如果需要对列表进行适当排序,则可以使用
1 | objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate)); |
如果您喜欢创建一个新的、已排序的序列,而不是就地排序,那么您可以使用Linq的
要在不使用linq on.net2.0的情况下执行此操作:
1 2 3 4 5 6 7 | List<Order> objListOrder = GetOrderList(); objListOrder.Sort( delegate(Order p1, Order p2) { return p1.OrderDate.CompareTo(p2.OrderDate); } ); |
如果你在.net3.0上,那么lukeh的答案就是你想要的。
要对多个属性进行排序,您仍然可以在委托中进行排序。例如:
1 2 3 4 5 6 7 8 9 10 11 | 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; } ); |
这将为您提供按降序排列的日期。
但是,我不建议粘贴代理,因为这意味着很多地方没有代码重用。您应该实现一个
1 2 3 4 5 6 7 8 9 10 11 12 | 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类,只需实例化它并将其传递给排序方法:
1 2 |
订购列表的最简单方法是使用
1 2 | List<Order> objListOrder = source.OrderBy(order => order.OrderDate).ToList(); |
如果要按多个列排序,如SQL查询。
1 | ORDER BY OrderDate, OrderId |
要实现这一点,您可以使用
1 2 | List<Order> objListOrder = source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList(); |
在没有Linq的情况下进行,正如您所说:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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()。
经典的面向对象解决方案
首先,我必须屈从于林肯的敬畏……既然我们已经把它弄走了
吉米霍夫答案的变化。使用泛型时,
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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"(或逻辑运算符,您的选择)将颠倒排序顺序。
//用于GridView的完全通用排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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); } |
下面是一个通用的Linq扩展方法,它不会创建列表的额外副本:
1 2 3 4 5 | 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))); } |
使用它:
1 | myList.Sort(x=> x.myProperty); |
我最近又建立了一个接受
1 2 3 4 5 | 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))); } |
使用LINQ
1 2 3 4 5 6 7 | objListOrder = GetOrderList() .OrderBy(o => o.OrderDate) .ToList(); objListOrder = GetOrderList() .OrderBy(o => o.OrderId) .ToList(); |
1 2 3 4 5 6 7 | //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; |
对于属性选择,您可以做一些更一般的事情,但是对于您选择的类型,在您的案例"顺序"中要具体说明:
编写一个通用函数:
1 2 3 4 5 6 | public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector) { return (from order in orders orderby propertySelector(order) select order).ToList(); } |
然后这样使用:
1 | var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate); |
您可以更通用,并为要订购的产品定义一个打开的类型:
1 2 3 4 5 6 | public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector) { return (from item in collection orderby propertySelector(item) select item).ToList(); } |
用同样的方法:
1 | var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate); |
这是一种愚蠢的、不必要的、复杂的执行LINQ样式"orderby"的方式,但它可能会给你一个线索,告诉你如何用一般的方式实现它。
请让我用一些示例代码@lukeh完成答案,因为我已经测试了它,我相信它可能对某些人有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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 ... } |
罗杰版本的改进。
GetDynamicSortProperty的问题在于,它只获取属性名,但如果在GridView中使用NavigationProperties,会发生什么?它将发送一个异常,因为它发现空值。
例子:
"employee.company.name;"将崩溃…因为只允许将"name"作为参数来获取其值。
这里有一个改进的版本,允许我们按导航属性排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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; } } |
1 2 3 | var obj = db.Items.Where... var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID)); |
基于GenericTypeTea的比较器:我们可以通过添加排序标志来获得更多的灵活性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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)。要设置其排序属性:
1 2 3 4 | MyOrderingClass comparer = new MyOrderingClass(); comparer.DateDescending = ...; comparer.OrderIdDescending = ...; orderList.Sort(comparer); |
上面的答案对我来说都不够一般,所以我做了一个:
1 2 3 4 5 6 7 8 | 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=对象内的属性
任何使用可以为空类型的
利用Linq
1 2 | List<Order> objListOrder=new List<Order> (); objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList(); |
从性能角度来看,最好是使用排序列表,以便在数据添加到结果时对其进行排序。其他方法至少需要对数据进行一次额外的迭代,而且大多数方法都会创建数据的副本,因此不仅会影响性能,还会影响内存的使用。可能不是几百个元素的问题,而是数千个元素的问题,特别是在许多并发请求可能同时进行排序的服务中。查看system.collections.generic命名空间,选择一个具有排序功能的类,而不是列表。
并且尽可能避免使用反射的一般实现,这也会导致性能问题。