如何通过对象中的属性对List<T>进行排序

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

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

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

如何在C#中执行此操作?

1666051 次浏览

使用LINQ

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

使用LiNQ#0

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

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

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

如果您需要就地对列表进行排序,那么您可以使用#0方法,传递#1委托:

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

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

排序列表的最简单方法是使用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();

要在没有LINQ on. Net2.0的情况下执行此操作:

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

如果你在. Net3.0上,那么LukeH的回答就是你想要的。

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

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方法。参见这里

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;}}

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

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

没有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()

//Get data from database, then sort list by staff name:
List<StaffMember> staffList = staffHandler.GetStaffMembers();
var sortedList = from staffmember in staffListorderby staffmember.Name ascendingselect staffmember;

//完全通用的排序,用于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 dataorderby GetDynamicSortProperty(n, sortExpression) ascendingselect n).ToList();}else if (sortDirection == "Descending"){data_sorted = (from n in dataorderby GetDynamicSortProperty(n, sortExpression) descendingselect n).ToList();
}
return data_sorted;
}
public object GetDynamicSortProperty(object item, string propName){//Use reflection to get order typereturn item.GetType().GetProperty(propName).GetValue(item, null);}

罗杰版本的改进。

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

示例:

因为只允许“Name”作为参数来获取它的值。

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

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

}

这是一个通用的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<U>的附加程序,以便您可以自定义比较。当我需要进行自然字符串排序时,这派上了用场:

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)));}

从性能的角度来看,最好是使用排序列表,以便数据在添加到结果时进行排序。其他方法需要对数据进行至少一次额外的迭代,大多数方法会创建数据的副本,因此不仅性能而且内存使用也会受到影响。查看System. Collection. Generic命名空间并选择一个使用排序而不是List的类。

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

经典的面向对象解决方案

首先,我必须屈从于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”(或逻辑运算符,你的选择)会颠倒排序顺序。

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

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(而不是比较器
为了设置其排序属性:

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

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

将您的函数编写为泛型函数:

public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector){return (from order in ordersorderby 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 collectionorderby propertySelector(item)select item).ToList();}

并以相同的方式使用它:

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

这是一种愚蠢的、不必要的、复杂的LINQ风格的“OrderBy”方法,但它可能会给你一个线索,告诉你如何以通用的方式实现它

请让我用一些示例代码完成@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 ...}

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

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=#对象中的属性

任何使用可空类型的人,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));

假设您有以下代码,在该代码中,我们有一个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);
}}

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

只是回到问题。如果你想排序这个序列的列表"1""10""100""200""2""20""3""30""300"并得到排序的项目在这个形式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);
  • 如果您需要对问题实体中的字符串ID进行排序

  • 解析Id后使用Sort函数和委托对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));

我为List<T>创建了这个扩展方法。

扩展方法将您希望排序的属性作为解析后的字符串,然后使用List<T>OrderBy方法。然后它将原始列表的每个索引设置为有序列表的相同索引。

public static class ListExtensions {public static void SortBy<T>(this List<T> list, string property, bool reverse = false) {List<T> ordered = list.OrderBy(obj => obj.GetType().GetProperty(property).GetValue(obj, null)).ToList();            
for (int i = 0; i < list.Count; i++)list[i] = reverse ? ordered[list.Count - 1 - i] : ordered[i];}}

如果列表中的对象具有属性Name,则按以下方式对列表testList进行排序:

//For normal sorting ordertestList.SortBy("Name");//For reverse sorting ordertestList.SortBy("Name", true);

我建议您将SortBy的名称更改为类似于Prefix_SortBy的名称。以防止在导入另一个库时发生潜在的冲突。

我知道这种方法适用于字母顺序和数字排序。它的排序能力可能有限,但操作非常简单。

如果有一些重大缺陷或问题,请告诉我,我已经编写C#大约3个月了。

最好的问候