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

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

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

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

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

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

14 ответов


как насчет использования LINQ для объектов для сортировки для вас?

скажи, что у тебя есть 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так же, как и список. В качестве бонуса вы можете перегрузить LINQ OrderBy метод расширения, используя ту же технику, так что используете ли вы Список.Рода, То IList.Рода, или 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> to 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 интерфейс.


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


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

у меня два ILists тот же тип возвращаемых 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.


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

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