Come ordinare una lista da una proprietà nell’object

Ho una class chiamata Order che ha proprietà come OrderId , OrderId , Quantity e Total . Ho una lista di questa class di Order :

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

Ora voglio ordinare l’elenco in base a una proprietà dell’object Order , ad esempio ho bisogno di ordinarlo in base alla data dell’ordine o all’ID ordine.

Come posso farlo in C #?

Il modo più semplice a cui riesco a pensare è usare Linq:

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

Se è necessario ordinare l’elenco sul posto, è ansible utilizzare il metodo Sort , passando un delegato di Comparison :

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

Se si preferisce creare una sequenza nuova, ordinata piuttosto che ordinare sul posto, è ansible utilizzare il metodo OrderBy di LINQ, come menzionato nelle altre risposte.

Per farlo senza LINQ su .Net2.0:

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

Se sei su .Net3.0, la risposta di LukeH è ciò che cerchi.

Per ordinare su più proprietà, puoi ancora farlo all’interno di un delegato. Per esempio:

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

Questo ti darebbe date ascendenti con ordini decrescenti .

Tuttavia, non consiglierei di attaccare i delegati poiché significherebbe molti posti senza riutilizzo del codice. Dovresti implementare un IComparer e trasferirlo nel tuo metodo Sort . Vedi qui

 public class MyOrderingClass : IComparer { 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; } } 

Quindi, per utilizzare questa class IComparer, basta istanziarla e passarla al metodo Sort:

 IComparer comparer = new MyOrderingClass(); orderList.Sort(comparer); 

Il modo più semplice per ordinare un elenco è utilizzare OrderBy

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

Se si desidera ordinare per più colonne come seguendo SQL Query.

 ORDER BY OrderDate, OrderId 

Per raggiungere questo objective, puoi utilizzare ThenBy come segue.

  List objListOrder = source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList(); 

Farlo senza Linq come hai detto:

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

Quindi chiama semplicemente .sort () nella tua lista di ordini

Una soluzione classica orientata agli oggetti

Per prima cosa devo genuflessi alla bellezza di LINQ … Ora che ce l’abbiamo tolta

Una variazione sulla risposta di JimmyHoffa. Con i generici il parametro CompareTo diventa sicuro.

 public class Order : IComparable { 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 myOrders.Sort(); 

Questa sorta di default è ovviamente riutilizzabile. Questo è il motivo per cui ogni cliente non deve riscrivere in modo ridondante la logica di ordinamento. Scambiando “1” e “-1” (o gli operatori logici, a tua scelta) si inverte l’ordinamento.

// Ordinamento completamente generico da utilizzare con un gridview

 public List Sort_List(string sortDirection, string sortExpression, List data) { List data_sorted = new List(); 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); } 

Ecco un metodo di estensione LINQ generico che non crea una copia aggiuntiva dell’elenco:

 public static void Sort(this List list, Func expression) where U : IComparable { list.Sort((x, y) => expression.Invoke(x).CompareTo(expression.Invoke(y))); } 

Per usarlo:

 myList.Sort(x=> x.myProperty); 

Di recente ho creato questo aggiuntivo che accetta un ICompare , in modo da poter personalizzare il confronto. Questo è stato utile quando ho avuto bisogno di fare una sorta di stringa naturale:

 public static void Sort(this List list, Func expression, IComparer comparer) where U : IComparable { list.Sort((x, y) => comparer.Compare(expression.Invoke(x), expression.Invoke(y))); } 

Utilizzando 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 staffList = staffHandler.GetStaffMembers(); var sortedList = from staffmember in staffList orderby staffmember.Name ascending select staffmember; 

Un miglioramento della versione di Roger.

Il problema con GetDynamicSortProperty è che ottengono solo i nomi delle proprietà ma cosa succede se nel GridView usiamo NavigationProperties? invierà un’eccezione, poiché trova nulla.

Esempio:

“Employee.Company.Name;” si arresta in modo anomalo … poiché consente solo il “Nome” come parametro per ottenere il suo valore.

Ecco una versione migliorata che ci consente di ordinare per Proprietà di navigazione.

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

Puoi fare qualcosa di più generico sulla selezione delle proprietà, ma essere specifico sul tipo da cui stai selezionando, nel tuo caso “Ordine”:

scrivi la tua funzione come generica:

 public List GetOrderList(IEnumerable orders, Func propertySelector) { return (from order in orders orderby propertySelector(order) select order).ToList(); } 

e quindi usarlo in questo modo:

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

Puoi essere ancora più generico e definire un tipo aperto per ciò che vuoi ordinare:

 public List OrderBy(IEnumerable collection, Func propertySelector) { return (from item in collection orderby propertySelector(item) select item).ToList(); } 

e usalo allo stesso modo:

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

Che è uno stupido e inutile modo complesso di fare uno stile LINQ ‘OrderBy’, ma potrebbe darti un’idea di come può essere implementato in modo generico

Per favore lasciatemi completare la risposta di @LukeH con un codice di esempio, poiché l’ho provato credo che possa essere utile per alcuni:

 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 objListOrder = new List(); 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 ... } 

Nessuna delle risposte di cui sopra è stata abbastanza generica per me, quindi ho fatto questo:

 var someUserInputStringValue = "propertyNameOfObject ie 'Quantity' or 'Date'"; var SortedData = DataToBeSorted .OrderBy(m => m.GetType() .GetProperties() .First(n => n.Name == someUserInputStringValue) .GetValue(m, null)) .ToList(); 

Attento su enormi set di dati però. È un codice facile ma potrebbe metterti nei guai se la collezione è enorme e il tipo di object della raccolta ha un numero elevato di campi. Il tempo di esecuzione è NxM dove:

N = numero di elementi nella collezione

M = numero di proprietà all’interno dell’object

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

Usa LiNQ OrderBy

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

Basato sul Comparatore di GenericTypeTea :
possiamo ottenere più flessibilità aggiungendo flag di ordinamento:

 public class MyOrderingClass : IComparer { 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; } } 

In questo scenario, è necessario istanziarlo come MyOrderingClass in modo esplicito (piuttosto che IComparer )
per impostare le sue proprietà di classificazione:

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

Dal punto di vista delle prestazioni, il migliore è utilizzare una lista ordinata in modo che i dati vengano ordinati in base al risultato. Altri approcci richiedono almeno un’iterazione aggiuntiva sui dati e la maggior parte crea una copia dei dati, quindi non solo le prestazioni, ma anche l’utilizzo della memoria ne risentirà. Potrebbe non essere un problema con un paio di centinaia di elementi, ma sarà con migliaia, soprattutto nei servizi in cui molte richieste simultanee possono fare lo smistamento allo stesso tempo. Dai uno sguardo allo spazio dei nomi System.Collections.Generic e scegli una class con l’ordinamento invece di Elenco.

Evitare implementazioni generiche utilizzando la riflessione quando ansible, questo può causare anche problemi di prestazioni.

Chiunque lavori con i tipi nullable, è necessario un valore per utilizzare CompareTo .

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