Сортировка IList в C#
Так что я наткнулся на интересную проблему сегодня. У нас есть веб-служба WCF, которая возвращает IList. Не очень большое дело, пока я не захотел разобраться.
оказывается, интерфейс IList не имеет встроенного метода сортировки.
Я в конечном итоге с помощью ArrayList.Adapter(list).Sort(new MyComparer())
метод решения проблемы, но это просто казалось мне немного "гетто".
Я играл с написанием метода расширения, а также с наследованием от IList и реализацией моего собственного метода Sort (), а также приведение к списку, но ни один из них не казался чрезмерно элегантным.
Итак, мой вопрос в том, есть ли у кого-нибудь элегантное решение для сортировки IList
14 ответов:
как насчет использования LINQ to Objects для сортировки для вас?
скажем, у вас есть
IList<Car>
, а в машине былоEngine
свойство, я считаю, что вы могли бы сортировать следующим образом:from c in list orderby c.Engine select c;
Edit: вам нужно быть быстрым, чтобы получить ответы здесь. Поскольку я представил немного другой синтаксис для других ответов, я оставлю свой ответ - однако другие представленные ответы одинаково действительны.
вы можете использовать LINQ:
using System.Linq; IList<Foo> list = new List<Foo>(); IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar); IList<Foo> sortedList = sortedEnum.ToList();
этот вопрос вдохновил меня написать сообщение в блоге:http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/
Я думаю, что в идеале .NET Framework будет включать статический метод сортировки, который принимает IList
, но следующим лучшим решением является создание собственного метода расширения. Это не слишком сложно создать несколько методов, которые позволят вам сортировать IList , как и List . В качестве бонуса вы можете перегрузить LINQ OrderBy метод расширения с использованием той же техники, так что ли вы используете список.Сорт, Илист.Рода, или IEnumerable.OrderBy, вы можете использовать тот же самый синтаксис. 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); } }
С помощью этих расширений, сортировать IList так же, как вы бы список:
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));
там больше информации в сообщении:http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/
вам придется сделать что-то вроде этого, я думаю (преобразовать его в более конкретный тип).
возможно, возьмите его в список T, а не ArrayList, чтобы вы получили безопасность типов и больше вариантов реализации компаратора.
принятый ответ @DavidMills довольно хорош, но я думаю, что его можно улучшить. Во-первых, нет необходимости определять
ComparisonComparer<T>
класс, когда фреймворк уже включает статический методComparer<T>.Create(Comparison<T>)
. Этот метод можно использовать для созданияIComparison
на лету.кроме того, он ставит под
IList<T>
доIList
который потенциально может быть опасным. В большинстве случаев, что я видел,List<T>
, который реализуетIList
используется за кулисами для реализацииIList<T>
, но это не гарантируется и может привести к хрупкому коду.наконец, перегруженный
List<T>.Sort()
метод имеет 4 подписи и только 2 из них реализованы.
List<T>.Sort()
List<T>.Sort(Comparison<T>)
List<T>.Sort(IComparer<T>)
List<T>.Sort(Int32, Int32, IComparer<T>)
ниже класс реализует все 4
List<T>.Sort()
подписи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]; } } }
использование:
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)); }
идея в том, чтобы использовать функциональность базового
List<T>
для сортировки, если это возможно. Опять же, большинствоIList<T>
реализации, которые я видел, используют это. В случае, когда базовая коллекция имеет другой тип, резервное копирование для создания нового экземпляраList<T>
С элементами из списка ввода используйте его для сортировки, а затем скопируйте результаты обратно в список ввода. Это будет работать даже если входной список не реализуетIList
интерфейс.
преобразовать
IList
наList<T>
или некоторые другие общие коллекции, а затем вы можете легко запросить / отсортировать его с помощьюSystem.Linq
пространство имен (он будет поставлять кучу методов расширения)
нашел эту тему, когда я искал решение точной проблемы, описанной в исходном сообщении. Однако ни один из ответов полностью не соответствовал моей ситуации. Ответ Броди был довольно близок. Вот моя ситуация и решение, которое я нашел для него.
у меня есть два IList того же типа, возвращенных NHibernate и появились два IList в один, следовательно, необходимость сортировки.
как сказал Броди, я реализовал ICompare на объекте (ReportFormat), который является тип моего Илиста:
public class FormatCcdeSorter:IComparer<ReportFormat> { public int Compare(ReportFormat x, ReportFormat y) { return x.FormatCode.CompareTo(y.FormatCode); } }
затем я преобразую объединенный IList в массив того же типа:
ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList
затем отсортировать массив:
Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer
так как одномерный массив реализует интерфейс
System.Collections.Generic.IList<T>
, массив может быть использован так же, как исходный IList.
полезно для сортировки сетки этот метод сортирует список на основе имен свойств. Как следует из примера.
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(); } } }
вот пример использования более сильного набора текста. Не уверен, что это обязательно лучший способ.
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; } }
функции Cast-это просто переопределение метода расширения, который поставляется с 3,5 написано как обычный статический метод. Это довольно некрасиво и многословно, к сожалению.
в VS2008, когда я нажимаю на ссылку на службу и выбираю "настроить ссылку на службу", есть возможность выбрать, как клиент де-сериализует списки, возвращенные из службы.
Примечательно, что я могу выбирать между системой.Массив, Система.Коллекции.ArrayList и системы.Коллекции.Родовой.Список
using System.Linq; var yourList = SomeDAO.GetRandomThings(); yourList.ToList().Sort( (thing, randomThing) => thing.CompareThisProperty.CompareTo( randomThing.CompareThisProperty ) );
прикольно !трущобы.
нашел хороший пост на этом и думал, что я поделюсь. посмотреть здесь
в принципе.
вы можете создать следующие классы class и 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); } }
тогда, если у вас есть IList, вы можете отсортировать его следующим образом.
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());
но проверьте этот сайт для получения дополнительной информации... посмотреть здесь
это правильное решение?
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();
результат был: IList Б Один C
список Один Б C
IList снова Один Б 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));**