J'ai une classe appelée Order
qui a des propriétés telles que OrderId
, OrderDate
, Quantity
et Total
. J'ai une liste de cette classe Order
:
List<Order> objListOrder = new List<Order>();
GetOrderList(objListOrder); // fill list of orders
Maintenant, je souhaite trier la liste en fonction d'une propriété de l'objet Order
. Par exemple, je dois la trier en fonction de la date de la commande ou de l'ID de la commande.
Comment puis-je faire cela en C #?
Le moyen le plus simple auquel je puisse penser est d'utiliser Linq:
List<Order> SortedList = objListOrder.OrderBy(o=>o.OrderDate).ToList();
Si vous devez trier la liste sur place, vous pouvez utiliser la méthode Sort
, en passant un Comparison<T>
délégué:
objListOrder.Sort((x, y) => x.OrderDate.CompareTo(y.OrderDate));
Si vous préférez créer une nouvelle séquence triée plutôt que de trier sur place, vous pouvez utiliser la méthode LINQ OrderBy
, comme indiqué dans les autres réponses.
Pour faire cela sans LINQ sur .Net2.0:
List<Order> objListOrder = GetOrderList();
objListOrder.Sort(
delegate(Order p1, Order p2)
{
return p1.OrderDate.CompareTo(p2.OrderDate);
}
);
Si vous êtes sur .Net3.0, alors LukeH réponse est ce que vous recherchez.
Pour trier sur plusieurs propriétés, vous pouvez toujours le faire au sein d'un délégué. Par exemple:
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;
}
);
Cela vous donnerait des dates croissantes avec décroissantes orderIds.
Cependant, je ne recommanderais pas de coller des délégués car cela signifierait beaucoup d'endroits sans réutilisation du code. Vous devez implémenter un IComparer
et le transmettre à votre méthode Sort
. Voir ici .
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;
}
}
Et puis, pour utiliser cette classe IComparer, il suffit de l’instancier et de la transmettre à votre méthode de tri:
IComparer<Order> comparer = new MyOrderingClass();
orderList.Sort(comparer);
Le moyen le plus simple de commander une liste consiste à utiliser OrderBy
name__
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ToList();
Si vous souhaitez commander par plusieurs colonnes comme suit SQL Query.
ORDER BY OrderDate, OrderId
Pour ce faire, vous pouvez utiliser ThenBy
comme suit.
List<Order> objListOrder =
source.OrderBy(order => order.OrderDate).ThenBy(order => order.OrderId).ToList();
Le faire sans Linq comme vous l'avez dit:
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;
}
}
Ensuite, appelez simplement .sort () sur votre liste de commandes
ne solution classique orientée objet
Tout d'abord, je dois faire une génuflexion à la génialité de LINQ ... Maintenant que nous avons réglé le problème
Une variation sur la réponse de JimmyHoffa. Avec les génériques, le paramètre CompareTo
devient de type sûr.
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();
Cette capacité de tri par défaut est bien sûr réutilisable. En d'autres termes, chaque client n'a pas à réécrire de manière redondante la logique de tri. Échanger les "1" et "-1" (ou les opérateurs logiques, votre choix) inverse l'ordre de tri.
// Tri totalement générique à utiliser avec un 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);
}
Voici une méthode d’extension LINQ générique qui ne crée pas de copie supplémentaire de la liste:
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)));
}
Pour l'utiliser:
myList.Sort(x=> x.myProperty);
J'ai récemment construit cette version supplémentaire qui accepte un ICompare<U>
, afin que vous puissiez personnaliser la comparaison. Cela m'a aidé lorsque j'ai eu besoin de faire une sorte de chaîne 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)));
}
Utiliser 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<StaffMember> staffList = staffHandler.GetStaffMembers();
var sortedList = from staffmember in staffList
orderby staffmember.Name ascending
select staffmember;
Une version améliorée de la version de Roger.
Le problème avec GetDynamicSortProperty est que seuls les noms de propriété sont obtenus, mais que se passe-t-il si dans GridView, nous utilisons NavigationProperties? il enverra une exception, car il trouve null.
Exemple:
"Employee.Company.Name;" va planter ... car n'autorise que "Nom" en tant que paramètre pour obtenir sa valeur.
Voici une version améliorée qui nous permet de trier par Propriétés de navigation.
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;
}
}
Vous pouvez faire quelque chose de plus générique sur la sélection des propriétés tout en précisant le type de votre choix, dans votre cas, "Commande":
écrivez votre fonction en tant que générique:
public List<Order> GetOrderList<T>(IEnumerable<Order> orders, Func<Order, T> propertySelector)
{
return (from order in orders
orderby propertySelector(order)
select order).ToList();
}
et ensuite l'utiliser comme ça:
var ordersOrderedByDate = GetOrderList(orders, x => x.OrderDate);
Vous pouvez être encore plus générique et définir un type ouvert pour ce que vous voulez commander:
public List<T> OrderBy<T,P>(IEnumerable<T> collection, Func<T,P> propertySelector)
{
return (from item in collection
orderby propertySelector(item)
select item).ToList();
}
et l'utiliser de la même manière:
var ordersOrderedByDate = OrderBy(orders, x => x.OrderDate);
Ce qui est une manière stupide et inutile de faire un 'OrderBy' dans le style LINQ, mais cela peut vous donner une idée de la façon dont cela peut être implémenté de manière générique.
Laissez-moi compléter la réponse par @LukeH avec un exemple de code, car je l’ai testé, je pense que cela pourrait être utile pour certains:
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));
Aucune des réponses ci-dessus n'était assez générique pour moi alors j'ai fait celle-ci:
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();
Attention cependant aux jeux de données massifs. C'est un code facile, mais qui pourrait vous causer des problèmes si la collection est volumineuse et si le type d'objet de la collection comporte un grand nombre de champs. Le temps d'exécution est NxM où:
N = nombre d'éléments dans la collection
M = # de propriétés dans l'objet
Toute personne travaillant avec des types nullables, Value
doit utiliser CompareTo
.
objListOrder.Sort((x, y) => x.YourNullableType.Value.CompareTo(y.YourNullableType.Value));
Utiliser LiNQ OrderBy
List<Order> objListOrder=new List<Order> ();
objListOrder=GetOrderList().OrderBy(o=>o.orderid).ToList();
Basé sur GenericTypeTea 's Comparer:
nous pouvons obtenir plus de flexibilité en ajoutant des drapeaux de tri:
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; }
}
Dans ce scénario, vous devez l'instancier en tant que MyOrderingClass explicitement (plutôt que IComparer)
afin de définir ses propriétés de tri:
MyOrderingClass comparer = new MyOrderingClass();
comparer.DateDescending = ...;
comparer.OrderIdDescending = ...;
orderList.Sort(comparer);
Du point de vue des performances, le mieux consiste à utiliser une liste triée pour que les données soient triées au fur et à mesure de l'ajout au résultat. D'autres approches nécessitent au moins une itération supplémentaire sur les données et la plupart en créent une copie, ce qui affecte non seulement les performances, mais également l'utilisation de la mémoire. Cela pourrait ne pas être un problème avec quelques centaines d’éléments, mais plutôt avec des milliers, en particulier dans les services où plusieurs demandes simultanées peuvent faire le tri en même temps. Examinez l'espace de noms System.Collections.Generic et choisissez une classe avec tri au lieu de List.
Et évitez les implémentations génériques utilisant la réflexion lorsque cela est possible, cela peut également entraîner des problèmes de performances.