Сортировка списка IList в C#

Итак, сегодня я столкнулся с интересной проблемой. У нас есть веб-служба WCF, которая возвращает список IList. Ничего особенного, пока я не разобрался.

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

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

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

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

Ответов (15)

Решение

How about using LINQ To Objects to sort for you?

Say you have a IList<Car>, and the car had an Engine property, I believe you could sort as follows:

from c in list
orderby c.Engine
select 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));**

Принятый ответ @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> интерфейса:

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 интерфейс.

Это выглядит НАМНОГО ПРОСТО, если вы спросите меня. Это ОТЛИЧНО работает для меня.

Вы можете использовать Cast (), чтобы изменить его на IList, затем используйте OrderBy ():

    var ordered = theIList.Cast<T>().OrderBy(e => e);

ГДЕ T - это тип, например. Model.Employee или Plugin.ContactService.Shared.Contact

Затем вы можете использовать цикл for и его DONE.

  ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }

Convert your IList into List<T> or some other generic collection and then you can easily query/sort it using System.Linq namespace (it will supply bunch of extension methods)

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

using System.Linq;

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

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

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

Вот пример более строгого набора. Не уверен, что это лучший способ.

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, когда я щелкаю ссылку на службу и выбираю «Настроить ссылку на службу», есть возможность выбрать способ десериализации списков, возвращаемых службой, клиентом.

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

Нашел хороший пост по этому поводу и подумал, что поделюсь. Посмотрите ЗДЕСЬ

По сути.

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

Но зайдите на этот сайт для получения дополнительной информации ... Проверьте это ЗДЕСЬ

using System.Linq;

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

Красиво! Гетто.

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

У меня есть два списка IList одного и того же типа, возвращенные 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

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

Это верное решение?

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

Список A B C

Iist еще раз A B C

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

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

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

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

    // Sorts in IList<T> in place, when T is IComparable<T>
    public static void Sort<T>(this IList<T> list) where T: IComparable<T>
    {
        Comparison<T> comparison = (l, r) => l.CompareTo(r);
        Sort(list, 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/