首页 文章

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

提问于
浏览
963

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

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

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

我怎样才能在C#中做到这一点?

19 回答

  • 204
    var obj = db.Items.Where...
    
    var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
    
  • 3

    您可以对属性选择做一些更通用的事情,但在您的案例“订单”中,您可以选择具体的类型:

    将您的函数编写为通用函数:

    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'的愚蠢不必要的复杂方式,但它可能会给你一个如何以通用方式实现它的线索

  • 3

    利用LiNQ OrderBy

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

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

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

    //用于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);
        }
    
  • 1417
    //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;
    
  • 3

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

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

  • 1

    A Classical Object Oriented Solution

    首先,我必须接受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”(或逻辑运算符,您的选择)会反转排序顺序 .

  • 3

    使用LINQ

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

    任何使用可空类型的人, Value 都需要使用 CompareTo .

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

  • 84

    这是一个通用的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)));
    }
    
  • 1

    请让@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 ...
    }
    
  • 0

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

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

    如果您想按照SQL Query之类的多个列进行排序 .

    ORDER BY OrderDate, OrderId
    

    为此,您可以使用 ThenBy ,如下所示 .

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

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

    基于 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);
    
  • 3

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

    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 =对象内的属性数

  • 21

    如果需要就地排序列表,则可以使用Sort方法,传递Comparison<T>委托:

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

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

  • 549

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

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

    如果你're on .Net3.0, then LukeH' s 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;
        }
    );
    

    这将为您提供 ascending 日期 descending 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);
    
  • 2

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

相关问题