如何按对象中的属性对List <T>进行sorting
我有一个名为Order
的类,它具有OrderId
, OrderDate
, Quantity
和Total
等属性。 我有这个Order
类的列表:
List<Order> objListOrder = new List<Order>(); GetOrderList(objListOrder); // fill list of orders
现在我想基于Order
对象的一个属性对列表进行sorting,例如我需要按订单date或订单ID进行sorting。
我怎么能在C#中做到这一点?
我能想到的最简单的方法是使用Linq:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
如果你需要就地sorting列表,那么你可以使用Sort
方法,传递一个Comparison<T>
委托:
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
如果您更喜欢创build一个新的sorting序列,而不是就地sorting,那么您可以使用LINQ的OrderBy
方法,如其他答案中所述。
在.Net2.0上不使用LINQ来做到这一点:
List<Order> objListOrder = GetOrderList(); objListOrder.Sort( delegate(Order p1, Order p2) { return p1.OrderDate.CompareTo(p2.OrderDate); } );
如果你使用的是.Net3.0,那么LukeH的回答就是你要的。
要对多个属性进行sorting,您仍然可以在委托中进行sorting。 例如:
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; } );
这会给你升序date降序 orderIds。
不过,我不build议坚持代表,因为这将意味着很多地方没有代码重用。 你应该实现一个IComparer
并把它传递给你的Sort
方法。 看到这里 。
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类,只是实例化它,并将其传递给你的sorting方法:
IComparer<Order> comparer = new MyOrderingClass(); orderList.Sort(comparer);
如果你想要的东西
ORDER BY OrderDate, OrderId
然后尝试如下。
List<Order> objListOrder = source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
这样做没有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()
面向对象的经典解决scheme
首先,我必须感受LINQ的迷人之处….现在,我们已经得到了这一点
JimmyHoffa答案的变化。 在generics中, CompareTo
参数变成types安全的。
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();
这种默认的sortingfunction当然是可以重复使用的。 这是每个客户端不必冗余地重新编写sorting逻辑。 交换“1”和“-1”(或逻辑运算符,您的select)颠倒sorting顺序。
//完全通用的sorting与gridview一起使用
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扩展方法,不会创build列表的额外副本:
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);
我最近build立了这个额外的接受一个ICompare<U>
,以便您可以自定义比较。 当我需要做一个自然的stringsorting时,这个方法很方便:
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
objListOrder = GetOrderList() .OrderBy(o => o.OrderDate) .ToList(); objListOrder = GetOrderList() .OrderBy(o => o.OrderId) .ToList();
//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;
你可以做一些更通用的属性select,但具体关于你select的types,在你的情况'订单':
把你的函数写成一个通用的函数:
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);
你可以更通用的定义一个你想要订购的开放types:
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”的方式,但它可能会给你一个线索,如何以通用的方式实现
请让我用@LukeH用一些示例代码来完成答案,因为我已经testing了它,我相信它对于某些人可能是有用的:
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,会发生什么? 它会发送一个exception,因为它发现null。
例:
“Employee.Company.Name;”将崩溃…因为只允许“Name”作为参数来获取其值。
这是一个改进的版本,允许我们按照导航属性进行sorting。
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; } }
上面的答案都不是通用的,所以我做了这个:
var someUserInputStringValue = "propertyNameOfObject ie 'Quantity' or 'Date'"; var SortedData = DataToBeSorted .OrderBy(m => m.GetType() .GetProperties() .First(n => n.Name == someUserInputStringValue) .GetValue(m, null)) .ToList();
小心大量的数据集。 这是简单的代码,但如果集合是巨大的,集合的对象types有大量的字段可能会麻烦你。 运行时间是NxM其中:
N =收集中的元素数量
M =对象内的属性数量
利用LiNQ OrderBy
List<Order> objListOrder=new List<Order> (); objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
从性能angular度来看,最好的方法是使用sorting列表,以便数据在添加结果时进行sorting。 其他方法至less需要对数据进行一次额外的迭代,而且大多数情况下都会创build数据的副本,这样不仅性能而且内存使用也会受到影响。 可能不是几百个元素的问题,但会有数千个问题,特别是在许多并发请求可能同时进行sorting的服务中。 看看System.Collections.Generic命名空间,并select一个类,而不是列表。
尽可能避免使用reflection的generics实现,这也会导致性能问题。
基于GenericTypeTea的比较器:
我们可以通过添加sorting标志来获得更多的灵活
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 )
为了设置它的sorting属性:
MyOrderingClass comparer = new MyOrderingClass(); comparer.DateDescending = ...; comparer.OrderIdDescending = ...; orderList.Sort(comparer);
任何使用可空types的人都必须使用Value
来使用CompareTo
。
objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
var obj = db.Items.Where... var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));