Tri des éléments en C#
alors j'ai rencontré un problème intéressant aujourd'hui. Nous avons un service web de la WCF qui retourne un IList. Pas grand chose jusqu'à ce que je veuille arranger ça.
S'avère que l'interface IList n'a pas une méthode de tri intégrée.
j'ai fini par utiliser la méthode ArrayList.Adapter(list).Sort(new MyComparer())
pour résoudre le problème mais ça me semblait un peu" ghetto".
j'ai joué avec l'écriture d'une méthode d'extension, également avec l'héritage de IList et mettre en œuvre ma propre méthode de Sort() ainsi que le casting à une liste mais aucun de ceux-ci ne semblait trop élégant.
ainsi ma question Est, est-ce que quelqu'un a une solution élégante pour trier un IList
14 réponses
Que Diriez-vous D'utiliser LINQ pour trier des objets pour vous?
dites que vous avez un IList<Car>
, et la voiture avait un Engine
propriété, je crois que vous pourriez trier comme suit:
from c in list
orderby c.Engine
select c;
Edit: Vous avez besoin d'être rapide pour obtenir des réponses ici. Comme j'ai présenté une syntaxe légèrement différente des autres réponses, je vais laisser ma réponse - cependant, les autres réponses présentées sont également valables.
vous pouvez utiliser LINQ:
using System.Linq;
IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
cette question m'a inspiré pour écrire un billet de blog: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way /
je pense que, idéalement, le .net Framework inclurait une méthode de tri statique qui accepte un IList
public static class SortExtensions
{
// Sorts an IList<T> in place.
public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
{
ArrayList.Adapter((IList)list).Sort(new ComparisonComparer<T>(comparison));
}
// Convenience method on IEnumerable<T> to allow passing of a
// Comparison<T> delegate to the OrderBy method.
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, Comparison<T> comparison)
{
return list.OrderBy(t => t, new ComparisonComparer<T>(comparison));
}
}
// Wraps a generic Comparison<T> delegate in an IComparer to make it easy
// to use a lambda expression for methods that take an IComparer or IComparer<T>
public class ComparisonComparer<T> : IComparer<T>, IComparer
{
private readonly Comparison<T> _comparison;
public ComparisonComparer(Comparison<T> comparison)
{
_comparison = comparison;
}
public int Compare(T x, T y)
{
return _comparison(x, y);
}
public int Compare(object o1, object o2)
{
return _comparison((T)o1, (T)o2);
}
}
avec ces extensions, triez votre liste comme vous le feriez une liste:
IList<string> iList = new []
{
"Carlton", "Alison", "Bob", "Eric", "David"
};
// Use the custom extensions:
// Sort in-place, by string length
iList.Sort((s1, s2) => s1.Length.CompareTo(s2.Length));
// Or use OrderBy()
IEnumerable<string> ordered = iList.OrderBy((s1, s2) => s1.Length.CompareTo(s2.Length));
il y a plus d'infos dans le post: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way /
vous allez devoir faire quelque chose comme ça je pense (le transformer en un type plus concret).
peut-être le prendre dans une liste de T plutôt que ArrayList, de sorte que vous obtenez la sécurité de type et plus d'options pour la façon dont vous mettez en œuvre le compareur.
la réponse acceptée par @DavidMills est assez bonne, mais je pense qu'elle peut être améliorée. D'une part, il n'est pas nécessaire de définir la classe ComparisonComparer<T>
lorsque le cadre comprend déjà une méthode statique Comparer<T>.Create(Comparison<T>)
. Cette méthode peut être utilisée pour créer un IComparison
à la volée.
Aussi, il jette IList<T>
à IList
qui a le potentiel d'être dangereux. Dans la plupart des cas que j'ai vu, List<T>
qui met en œuvre IList
est utilisé dans les coulisses pour mettre en œuvre IList<T>
, mais ce n'est pas garanti et peut conduire à code fragile.
enfin, la méthode surchargée List<T>.Sort()
a 4 signatures et seulement 2 d'entre elles sont implémentées.
-
List<T>.Sort()
-
List<T>.Sort(Comparison<T>)
-
List<T>.Sort(IComparer<T>)
-
List<T>.Sort(Int32, Int32, IComparer<T>)
la classe ci-dessous met en œuvre tous les 4 List<T>.Sort()
signatures pour l'interface IList<T>
:
using System;
using System.Collections.Generic;
public static class IListExtensions
{
public static void Sort<T>(this IList<T> list)
{
if (list is List<T>)
{
((List<T>)list).Sort();
}
else
{
List<T> copy = new List<T>(list);
copy.Sort();
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
{
if (list is List<T>)
{
((List<T>)list).Sort(comparison);
}
else
{
List<T> copy = new List<T>(list);
copy.Sort(comparison);
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, IComparer<T> comparer)
{
if (list is List<T>)
{
((List<T>)list).Sort(comparer);
}
else
{
List<T> copy = new List<T>(list);
copy.Sort(comparer);
Copy(copy, 0, list, 0, list.Count);
}
}
public static void Sort<T>(this IList<T> list, int index, int count,
IComparer<T> comparer)
{
if (list is List<T>)
{
((List<T>)list).Sort(index, count, comparer);
}
else
{
List<T> range = new List<T>(count);
for (int i = 0; i < count; i++)
{
range.Add(list[index + i]);
}
range.Sort(comparer);
Copy(range, 0, list, index, count);
}
}
private static void Copy<T>(IList<T> sourceList, int sourceIndex,
IList<T> destinationList, int destinationIndex, int count)
{
for (int i = 0; i < count; i++)
{
destinationList[destinationIndex + i] = sourceList[sourceIndex + i];
}
}
}
Utilisation:
class Foo
{
public int Bar;
public Foo(int bar) { this.Bar = bar; }
}
void TestSort()
{
IList<int> ints = new List<int>() { 1, 4, 5, 3, 2 };
IList<Foo> foos = new List<Foo>()
{
new Foo(1),
new Foo(4),
new Foo(5),
new Foo(3),
new Foo(2),
};
ints.Sort();
foos.Sort((x, y) => Comparer<int>.Default.Compare(x.Bar, y.Bar));
}
l'idée ici est de tirer parti de la fonctionnalité du List<T>
sous-jacent pour gérer le tri chaque fois que possible. Encore une fois, la plupart des implémentations IList<T>
que j'ai vu utilisent ceci. Dans le cas où la collection sous-jacente est un type différent, retombée à la création d'une nouvelle instance de List<T>
avec des éléments de la liste d'entrée, utilisez-le pour faire le tri, recopiez les résultats dans la liste des entrées. Cela fonctionnera même si la liste des entrées n'implémente pas l'interface IList
.
Convertissez votre IList
en List<T>
ou une autre collection générique et alors vous pouvez facilement le rechercher / le trier en utilisant System.Linq
namespace (il fournira tas de méthodes d'extension)
a trouvé ce fil pendant que je cherchais une solution au problème exact décrit dans le post original. Aucune réponse rencontré ma situation entièrement, cependant. La réponse de Brody était assez proche. Voici ma situation et la solution que j'ai trouvé.
j'ai deux Ilistes du même type retournés par NHibernate et ont émergé les deux Ilistes en un, d'où le besoin de tri.
comme Brody dit que j'ai mis en œuvre une Icom Pare sur l'objet (ReportFormat) qui est le type de mon IList:
public class FormatCcdeSorter:IComparer<ReportFormat>
{
public int Compare(ReportFormat x, ReportFormat y)
{
return x.FormatCode.CompareTo(y.FormatCode);
}
}
je convertis alors L'IList fusionné à un tableau du même type:
ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList
puis trier le tableau:
Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer
puisque le tableau unidimensionnel implémente l'interface System.Collections.Generic.IList<T>
, le tableau peut être utilisé tout comme L'IList original.
utile pour le tri de grille cette méthode trie la liste basée sur les noms de propriétés. Que suivre l'exemple.
List<MeuTeste> temp = new List<MeuTeste>();
temp.Add(new MeuTeste(2, "ramster", DateTime.Now));
temp.Add(new MeuTeste(1, "ball", DateTime.Now));
temp.Add(new MeuTeste(8, "gimm", DateTime.Now));
temp.Add(new MeuTeste(3, "dies", DateTime.Now));
temp.Add(new MeuTeste(9, "random", DateTime.Now));
temp.Add(new MeuTeste(5, "call", DateTime.Now));
temp.Add(new MeuTeste(6, "simple", DateTime.Now));
temp.Add(new MeuTeste(7, "silver", DateTime.Now));
temp.Add(new MeuTeste(4, "inn", DateTime.Now));
SortList(ref temp, SortDirection.Ascending, "MyProperty");
private void SortList<T>(
ref List<T> lista
, SortDirection sort
, string propertyToOrder)
{
if (!string.IsNullOrEmpty(propertyToOrder)
&& lista != null
&& lista.Count > 0)
{
Type t = lista[0].GetType();
if (sort == SortDirection.Ascending)
{
lista = lista.OrderBy(
a => t.InvokeMember(
propertyToOrder
, System.Reflection.BindingFlags.GetProperty
, null
, a
, null
)
).ToList();
}
else
{
lista = lista.OrderByDescending(
a => t.InvokeMember(
propertyToOrder
, System.Reflection.BindingFlags.GetProperty
, null
, a
, null
)
).ToList();
}
}
}
voici un exemple en utilisant le plus fort typage. Vous ne savez pas si c'est forcément de la meilleure manière.
static void Main(string[] args)
{
IList list = new List<int>() { 1, 3, 2, 5, 4, 6, 9, 8, 7 };
List<int> stronglyTypedList = new List<int>(Cast<int>(list));
stronglyTypedList.Sort();
}
private static IEnumerable<T> Cast<T>(IEnumerable list)
{
foreach (T item in list)
{
yield return item;
}
}
la fonction Cast n'est qu'une réimplantation de la méthode d'extension qui vient avec 3.5 écrite comme une méthode statique normale. Il est assez laid et verbeux malheureusement.
dans VS2008, lorsque je clique sur la référence de service et que je sélectionne" configurer la référence de Service", il y a une option pour choisir comment le client désiscrit les listes retournées du service.
notamment, je peux choisir entre le système.Tableau, Système.Collection.Liste de tableaux et de Système.Collection.Générique.Liste
trouvé un bon post sur ce et pensé que je partagerais. Check it out HERE
en gros.
vous pouvez créer la classe suivante et les Classes IComparer
public class Widget {
public string Name = string.Empty;
public int Size = 0;
public Widget(string name, int size) {
this.Name = name;
this.Size = size;
}
}
public class WidgetNameSorter : IComparer<Widget> {
public int Compare(Widget x, Widget y) {
return x.Name.CompareTo(y.Name);
}
}
public class WidgetSizeSorter : IComparer<Widget> {
public int Compare(Widget x, Widget y) {
return x.Size.CompareTo(y.Size);
}
}
alors si vous avez un IList, vous pouvez le trier comme ceci.
List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));
widgets.Sort(new WidgetNameSorter());
widgets.Sort(new WidgetSizeSorter());
mais consultez ce site pour plus d'informations... Check it out HERE
Est-ce une solution valable?
IList<string> ilist = new List<string>();
ilist.Add("B");
ilist.Add("A");
ilist.Add("C");
Console.WriteLine("IList");
foreach (string val in ilist)
Console.WriteLine(val);
Console.WriteLine();
List<string> list = (List<string>)ilist;
list.Sort();
Console.WriteLine("List");
foreach (string val in list)
Console.WriteLine(val);
Console.WriteLine();
list = null;
Console.WriteLine("IList again");
foreach (string val in ilist)
Console.WriteLine(val);
Console.WriteLine();
le résultat a été: IList B Un C
liste Un B C
IList again Un B C
try this **USE ORDER BY** :
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
}
private static IList<Employee> GetItems()
{
List<Employee> lst = new List<Employee>();
lst.Add(new Employee { Id = "1", Name = "Emp1" });
lst.Add(new Employee { Id = "2", Name = "Emp2" });
lst.Add(new Employee { Id = "7", Name = "Emp7" });
lst.Add(new Employee { Id = "4", Name = "Emp4" });
lst.Add(new Employee { Id = "5", Name = "Emp5" });
lst.Add(new Employee { Id = "6", Name = "Emp6" });
lst.Add(new Employee { Id = "3", Name = "Emp3" });
return lst;
}
**var lst = GetItems().AsEnumerable();
var orderedLst = lst.OrderBy(t => t.Id).ToList();
orderedLst.ForEach(emp => Console.WriteLine("Id - {0} Name -{1}", emp.Id, emp.Name));**