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


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

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

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

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

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

14 74

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 из них реализованы.

  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 пространство имен (он будет поставлять кучу методов расширения)

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

у меня есть два 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));**