我有一个名为类Order
具有的属性,例如OrderId
,OrderDate
,Quantity
,和Total
。我有这个Order
班的清单:
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
现在,我想根据Order
对象的一个属性对列表进行排序,例如,我需要按订单日期或订单ID对其进行排序。
我该如何在C#中做到这一点?
我有一个名为类Order
具有的属性,例如OrderId
,OrderDate
,Quantity
,和Total
。我有这个Order
班的清单:
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
现在,我想根据Order
对象的一个属性对列表进行排序,例如,我需要按订单日期或订单ID对其进行排序。
我该如何在C#中做到这一点?
Answers:
我能想到的最简单的方法是使用Linq:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
listWithObjects = listWithObjects.OrderByDescending(o => o.Status).ToList();
足以满足要求吗?
如果需要就地对列表进行排序,则可以使用Sort
方法,并传递一个Comparison<T>
委托:
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
如果您更喜欢创建一个新的,已排序的序列,而不是就地排序,则可以使用LINQ的OrderBy
方法,如其他答案所述。
x
和y
=>
。
Nullable<>
(以DateTime?
示例为例).Sort((x, y) => Nullable.Compare(x.OrderDate, y.OrderDate))
,它将把null视为所有非null值的前面。与完全相同.Sort((x, y) => Comparer<DateTime?>.Default.Compare(x.OrderDate, y.OrderDate)
。
要在.Net2.0上不使用LINQ的情况下执行此操作:
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;
}
}
然后要使用此IComparer类,只需实例化它并将其传递给您的Sort方法:
IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
IComparer
实现,从而为我们提供多态行为。
如您所说,无需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()
null
对as
演员表进行测试。这是的全部要点as
,因为(ha,ha)(Order)obj
失败时会引发异常。if(orderToCompare == null) return 1;
。
as
返回null
。但是至少空测试是正确的。
List<Order>
因此应确保类型与上一个列表匹配,as
因此2014年您可能没有写过bug,而避免了不必要的保护声明:)
List<Order>
; 但你和我都已经满足了自封装的程序员谁的潜假设是:“我写这个代码,以便它不会被用于错误的”
经典的面向对象解决方案
首先,我必须对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”(或您选择的逻辑运算符)将颠倒排序顺序。
this
对象大于null。为了排序,空对象引用“小于” this
对象。这就是我决定定义null排序方式的方式。
//用于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);
}
data.OrderBy()
。重新发明轮子要容易一些。
这是不创建列表的额外副本的通用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)));
}
OrderBy
内部完成所有这些操作。
void
本着这种精神编写扩展方法的另一种不错的方法:static class Extensions { public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector) { self.SortBy(keySelector, Comparer<TKey>.Default); } public static void SortBy<TSource, TKey>(this List<TSource> self, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) { self.Sort((x, y) => comparer.Compare(keySelector(x), keySelector(y))); } }
可以通过SortByDescending
方法进行补充。@Servy的注释也适用于我的代码!
//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;
罗杰版本的改进。
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;
}
}
您可以对属性选择做一些更通用的操作,但要具体针对要选择的类型(例如“订单”):
将您的函数编写为通用函数:
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”的愚蠢且不必要的复杂方法,但它可能为您提供了如何以通用方式实现的线索
请让我用一些示例代码来完成@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 obj = db.Items.Where...
var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
利用LiNQ OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
基于GenericTypeTea的Comparer:
我们可以通过添加排序标志来获得更大的灵活性:
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);
上述答案对我来说都不是通用的,所以我做了这个:
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 =对象中的属性数
从性能的角度来看,最好的方法是使用排序列表,以便在将数据添加到结果中时对其进行排序。其他方法至少需要对数据进行一次额外的迭代,并且大多数方法都会创建数据的副本,因此不仅性能也会受到内存使用的影响。可能不是成百上千个元素的问题,而是成千上万个元素的问题,尤其是在许多并发请求可能同时进行排序的服务中。看一下System.Collections.Generic命名空间,然后选择一个带有排序的类而不是List。
并且尽可能避免使用反射的通用实现,这也会导致性能问题。
假设您有以下代码,在此代码中,我们有一个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);
}
}
因此,您可以使用Composition委托来实现排序结构。