2008-08-19 2 views
68

Так что сегодня я столкнулся с интересной проблемой. У нас есть веб-сервис WCF, который возвращает IList. Не очень большое дело, пока я не хотел его сортировать.Сортировка IList в C#

Оказывается IList интерфейс не имеет метод сортировки встраивается.

Я закончил с использованием метода ArrayList.Adapter(list).Sort(new MyComparer()), чтобы решить эту проблему, но это только казалось, немного «гетто» для меня.

Я играл с наложением метода расширения, также с наследованием от IList и реализации моего собственного метода Sort(), а также отбрасывания в список, но ни один из них не казался слишком элегантным.

Так что мой вопрос, кто-нибудь есть элегантное решение сортировки IList

+0

Зачем вам возвращать IList в первую очередь? Из службы WCF? – DaeMoohn 2011-02-18 15:42:20

ответ

51

Как об использовании LINQ To Objects для сортировки для вас?

Скажем, у вас есть IList<Car>, и автомобиль был Engine собственность, я полагаю, вы могли бы сортировать следующим образом:

from c in list 
orderby c.Engine 
select c; 

Edit: Вы должны быть быстрым, чтобы получить ответы здесь. Поскольку я представил несколько иной синтаксис для других ответов, я оставлю свой ответ, однако другие представленные ответы одинаково верны.

+0

Он создаст новое перечислимое, что может быть нежелательно в некоторых сценариях. Вы не можете сортировать IList на месте через интерфейс, за исключением использования метода ArrayList.Adapter, насколько мне известно. – 2014-08-28 11:11:38

9

Вы будете иметь, чтобы сделать что-то подобное, что я думаю (преобразовать ее в более конкретный тип).

Возможно, возьмите его в список T, а не ArrayList, чтобы вы получили безопасность типов и больше возможностей для реализации компаратора.

2

Преобразование ваш IList в List<T> или какой-либо другой общей коллекции, а затем вы можете легко запросить/сортировать его с помощью System.Linq пространства имен (он будет поставлять кучу методов расширения)

+6

`IList ` реализует `IEnumerable ` и поэтому не нуждается в преобразовании для использования операций Linq. – 2010-07-13 22:43:17

56

Вы можете использовать LINQ:

using System.Linq; 

IList<Foo> list = new List<Foo>(); 
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar); 
IList<Foo> sortedList = sortedEnum.ToList(); 
0

Вот пример, используя более сильный ввод. Не уверен, что это обязательно лучший способ.

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 записываются как обычный статический метод. К сожалению, это довольно уродливо и многословно.

0

В VS2008, когда я нажимаю на ссылку службы и выбираю «Настроить сервисную ссылку», есть возможность выбрать, как клиент де-сериализует списки, возвращаемые службой.

Следует отметить, что я могу выбрать между System.Array, System.Collections.ArrayList и System.Collections.Generic.List

0

Нашли хороший пост об этом и думал, что я поделюсь. Check it out HERE

В принципе.

Вы можете создать следующий класс и 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()); 

Но заказ этот сайт для получения дополнительной информации ... Check it out HERE

0
using System.Linq; 

var yourList = SomeDAO.GetRandomThings(); 
yourList.ToList().Sort((thing, randomThing) => thing.CompareThisProperty.CompareTo(randomThing.CompareThisProperty)); 

Это довольно! Гетто.

1

Обнаружил эту тему, пока я искал решение проблемы, описанной в исходном сообщении. Однако ни один из ответов не удовлетворил мою ситуацию. Ответ Броди был довольно близок. Вот моя ситуация и решение, которые я нашел.

У меня есть два Илиста того же типа, которые были возвращены NHibernate и вывели два IList в один, отсюда необходимость сортировки.

Как Броуди сказал, что я осуществил ICompare на объект (ReportFormat), который является тип моей IList:

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 

Поскольку одномерный массив реализует I nterface System.Collections.Generic.IList<T>, массив можно использовать так же, как оригинальный IList.

+0

Это правильный способ сделать это. – user29964 2010-09-29 08:55:08

0

Действительно ли это подходящее решение?

 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 B C

Список B C

IList снова B C

+0

Действительно, если это действительно список . В некоторых случаях у вас есть другие типы, реализующие IList (например, простой массив), где downcast не работает. Жаль, что метод Sort() не является методом расширения для IList . – Cygon 2012-01-05 13:32:03

1

Полезно для сетки сортировки этот метод сортирует список основанные на именах свойств. Как следует из примера.

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

Этот вопрос вдохновил меня написать пост в блоге: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Я думаю, что, в идеале, .NET Framework будет включать в себя статический метод сортировки, который принимает IList <T>, но следующий лучший дело в том, чтобы создать свой собственный метод расширения. Не сложно создать пару методов, которые позволят вам отсортировать IList <T> так же, как и список <T>. В качестве бонуса вы можете перегрузить метод расширения LINQ OrderBy с использованием того же метода, чтобы использовать ли вы List.Sort, IList.Sort или 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/

+0

Правильный подход действительно заключался бы в том, чтобы предложить интерфейс `ISortableList ` (с методами сортировки части списка с использованием определенного компаратора), иметь `List ` реализовать его и иметь статический метод, который мог бы отсортировать любые ` IList `, проверив, реализовал ли он` ISortableList ` и, если нет, копировал его в массив, сортируя его, очищая `IList ` и повторно добавляя элементы. – supercat 2012-09-28 23:32:22

0
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));** 
4

Принятый ответ на @DavidMills является неплохо, но я думаю, что это можно улучшить. Во-первых, нет необходимости определять класс ComparisonComparer<T>, когда среда уже включает статический метод Comparer<T>.Create(Comparison<T>). Этот метод можно использовать для создания IComparison на лету.

Кроме того, он отличает IList<T> - IList, который может быть опасным. В большинстве случаев, которые я видел, List<T>, который реализует IList, используется за кулисами для реализации IList<T>, но это не гарантируется и может привести к хрупкому коду.

Наконец, перегруженный метод List<T>.Sort() имеет 4 подписи и только 2 из них реализованы.

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

В приведенной ниже класс реализует все 4 List<T>.Sort() подписи для интерфейса IList<T>:

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(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.

Смежные вопросы