У меня есть класс под названием Order, который имеет такие свойства, как OrderId, OrderDate, Quantity и Total. У меня есть список этого класса Order:

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

Теперь я хочу отсортировать список на основе одного свойства объекта Order, например, мне нужно отсортировать его по дате заказа или идентификатору заказа.

Как я могу сделать это на C #?

1459
Shyju 22 Июл 2010 в 17:13

22 ответа

Лучший ответ

Самый простой способ, который я могу придумать, - это использовать Linq:

List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
1999
Kyle Alons 20 Мар 2015 в 20:54

Если вам нужно отсортировать список на месте, вы можете использовать Sort метод, передающий Comparison<T> делегат:

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

Если вы предпочитаете создавать новую отсортированную последовательность, а не сортировать на месте, вы можете использовать LINQ OrderBy, как упоминалось в других ответах.

918
Kolappan N 27 Ноя 2018 в 08:50

Чтобы сделать это без LINQ в .Net2.0:

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

Если вы используете .Net3.0, тогда 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;
    }
);

Это даст вам восходящие даты с нисходящим идентификатором порядка.

Однако я бы не рекомендовал использовать делегатов, поскольку это будет означать, что во многих местах не будет повторного использования кода. Вы должны реализовать 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);
230
Community 23 Май 2017 в 11:47

Самый простой способ упорядочить список - использовать 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();
122
PSK 13 Июн 2018 в 07:41

Делая это без 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 () в своем списке заказов.

34
Jimmy Hoffa 30 Янв 2014 в 16:36

Классическое объектно-ориентированное решение

Сначала я должен преклонить колени перед великолепием LINQ .... Теперь, когда мы избавились от этого

Вариант ответа Джимми Хоффа. С универсальными шаблонами параметр 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» (или логические операторы на ваш выбор), порядок сортировки меняется на противоположный.

29
radarbob 11 Фев 2017 в 05:41

// Абсолютно общая сортировка для использования с сеткой

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);
    }
18
roger 13 Июн 2013 в 01:37

Использование LINQ

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

objListOrder = GetOrderList()
                   .OrderBy(o => o.OrderId)
                   .ToList();
6
Daniel A. White 22 Июл 2010 в 13:16
//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;
4
Waqas Ahmed 23 Ноя 2011 в 21:51

Пожалуйста, позвольте мне завершить ответ @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 ...
}
4
molbalga 19 Июл 2016 в 10:18

Улучшенная версия Роджера.

Проблема с GetDynamicSortProperty заключается в том, что можно получить только имена свойств, но что произойдет, если в GridView мы будем использовать NavigationProperties? он отправит исключение, так как найдет null.

Примере:

"Employee.Company.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;
        }


    } 
3
Miguel Becerra 4 Июл 2013 в 14:32

Воспользуйтесь LiNQ OrderBy

List<Order> objListOrder=new List<Order> ();
    objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
1
Pranay Rana 22 Июл 2010 в 13:16

На основе сравнения GenericTypeTea :
мы можем получить большую гибкость, добавив флаги сортировки:

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);  
1
Jack Griffin 6 Янв 2015 в 14:05

Ни один из приведенных выше ответов не был для меня достаточно общим, поэтому я сделал этот:

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 = количество свойств в объекте

1
itcropper 22 Авг 2016 в 18:45

Любой, кто работает с типами, допускающими значение NULL, Value должен использовать CompareTo.

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

1
Jude 19 Янв 2017 в 15:35

Предположим, у вас есть следующий код, в этом коде у нас есть класс 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
Seyedraouf Modarresi 2 Июн 2020 в 12:11

С точки зрения производительности лучше всего использовать отсортированный список, чтобы данные сортировались по мере добавления к результату. Другие подходы требуют по крайней мере одной дополнительной итерации данных, и большинство из них создают копию данных, поэтому будет затронута не только производительность, но и использование памяти. Может не быть проблемой с парой сотен элементов, но будет с тысячами, особенно в сервисах, где многие параллельные запросы могут выполнять сортировку одновременно. Взгляните на пространство имен System.Collections.Generic и выберите класс с сортировкой вместо List.

И избегайте общих реализаций с использованием отражения, когда это возможно, это также может вызвать проблемы с производительностью.

0
user3285954 31 Июл 2014 в 11:53

Привет, просто чтобы вернуться к вопросу. Если вы хотите отсортировать Список этой последовательности "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);
0
luka 28 Янв 2021 в 07:36
  • Если вам нужно отсортировать идентификатор, который является строкой в ​​сущности вопроса

  • Используйте функцию сортировки и делегат для сортировки идентификатора после его анализа. стоимость

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

0
Dev-lop-er 13 Май 2021 в 14:52

Вот общий метод расширения 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>, так что вы можете настроить сравнение. Это пригодилось, когда мне нужно было выполнить сортировку строк Natural:

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)));
}
7
Peter 8 Апр 2014 в 21:06

Вы можете сделать что-то более общее в отношении выбора свойств, но при этом указать тип, из которого вы выбираете, в вашем случае «Порядок»:

Напишите свою функцию как общую:

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
Danny Mor 2 Апр 2015 в 10:26
var obj = db.Items.Where...

var orderBYItemId = obj.OrderByDescending(c => Convert.ToInt32(c.ID));
3
Jevgenij Kononov 31 Май 2017 в 08:26