J'ai une boucle foreach que je souhaite modifier:
foreach (var line in lines.OrderBy(x=> x.ColA))
Si une condition est remplie, au lieu de commander par ColA, je souhaite passer une commande par ColB.
Je sais que cela pourrait être fait comme suit:
var orderLines = new List<OrderLines>(); if (condition) orderLines = lines.OrderBy(x => x.ColB).ToList(); else orderLines = lines.OrderBy(x => x.ColA).ToList(); ; foreach (var line in orderLines)
Mais je suis sûr qu'il existe une solution plus élégante.
4 Réponses :
Installez NuGet
System.Linq.Dynamic
et vous pouvez passer le nom de property
sous forme de string
à OrderBy
comme ci-dessous.
list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder")
utilisation.AsQueryable list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder")
. Où PropertyName
sera ColA
ColB
. Et SortOrder
sera ASC
DESC
.
using System.Linq.Dynamic;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
Pour .Net Core
installez NuGet
System.Linq.Dynamic.Core
.
using System.Linq.Dynamic.Core;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
Pour une meilleure pratique plutôt que de fournir PropertyName
sous forme de chaîne, utilisez nameof(Class.Property)
comme dans votre cas nameof(OrderLines.ColA)
. Donc, si vous modifiez la propriété ColA
, une erreur de Build
s'affichera et vous n'obtiendrez pas d' run time exception
.
foreach (var line in lines.AsQueryable().OrderBy(condition ? nameof(OrderLines.ColB) : nameof(OrderLines.ColA))
Merci pour la suggestion @Karan mais je ne suis pas trop enthousiaste
C'est probablement aussi bon que possible.
Rappelez-vous que derrière cette expression lambda, la magie se produit qui se lie (effectivement) à un Comparer<T>
où T dépend du type des colonnes comparées.
Rendre cela plus concis pourrait le rendre moins efficace. Plus précisément, la conversion et la comparaison de chaînes ralentissent et peuvent vous causer des problèmes (les entrées sont triées sur 1, 2, 3, ... 10, 11, ... par rapport à leurs chaînes sur "1", "10", "11") , ... "19", "2", "20", "21" ...).
Un "one-liner" n'est élégant que si son comportement est évident, sinon il est obscurci .
Votre code est correct. (OMI ;-)
Merci pour le commentaire @AlanK
Plusieurs solutions.
(1) Ne faites pas le ToList()
avant votre foreach, créez seulement le IEnumerable.
var result = lines.Where(orderLine => orderLine.Date.Year >= 2020) .OrderBy(this.checkBox1.IsChecked) .Select(orderLine => new { Id = orderLine.Id, Price = orderLine.Price, });
(2) Si vous comptez l'utiliser à plusieurs endroits, envisagez de créer une méthode d'extension. De cette façon, votre méthode ressemble à n'importe quelle autre méthode LINQ. Voir les méthodes d'extension démystifiées
IEnumerable<OrderLine> lines = ... foreach(var sortedOrderLine in lines.OrderBy(this.CheckBox1.IsChecked)) { ... }
Usage:
Si l'opérateur vérifie chexBox1, triez par colB, sinon triez par colA:
public static IEnumerable<OrderLine> OrderBy( this IEnumerable<OrderLine> source, bool condition) { return condition ? lines.OrderBy(orderLine => orderLine.ColB) : lines.OrderBy(orderLine => orderLine.ColA); }
Comme il s'agit d'une méthode d'extension de IEnumerable<OrderLine>
, vous pouvez même l' IEnumerable<OrderLine>
avec d'autres méthodes LINQ:
IEnumerable<OrderLines> orderLines = condition ? lines.OrderBy(orderLine => orderLine.ColB) : lines.OrderBy(orderLine => orderLine.ColA); foreach(OrderLine orderlLine in orderLines) {...}
Mais dans l'ensemble, cela ne vous fait pas économiser beaucoup de code. Le seul avantage serait de l'utiliser dans de nombreuses méthodes. Dans ce cas, une modification de la manière dont vous souhaitez appliquer la condition OrderBy doit être modifiée à un seul endroit. Mais encore une fois: si vous prévoyez de l'utiliser à un seul endroit, le déplacer vers une méthode distincte pourrait ne pas aider les lecteurs à comprendre ce qui se passe.
Merci pour votre commentaire approfondi @Harald Coppoolse
Comme @AlanK l'a mentionné, le plus proche que nous pouvons obtenir pour simplifier OrderBy
serait quelque chose comme:
Func<OrderLines, string> selector = (orderLine) => condition ? orderLine.ColB : orderLine.ColA; List<OrderLines> orderLines = lines.OrderBy(selector);
à condition que ColA
et ColB
soient du même type de données. Sinon, ce ne serait pas efficace en raison de la surcharge de la conversion du type de données.
Merci pour le commentaire @Zephyr
Dépend de la condition. Qu'Est-ce que c'est?
@juergend la condition est de vérifier quel programme a créé les lignes de commande. Par exemple, si (programme == "OrderEntry") puis Trier par colonne B sinon je veux qu'il trie par colonne A.
@SamoanProgrammer Est-ce que
lines.OrderBy(x => condition?x.ColA:x.ColB).ToList()
ne répond pas à voslines.OrderBy(x => condition?x.ColA:x.ColB).ToList()
?@ TấnNguyên non désolé, j'ai essayé cela et j'ai obtenu une erreur car ColA est une chaîne et ColB est un int.
La façon dont vous l'avez fait est claire et compréhensible. Laissez-le tel quel.
Merci pour le commentaire @mjwills
Est-ce que cela répond à votre question? Commande LINQ dynamiqueBy sur IEnumerable <T> / IQueryable <T>