Сортировка IList в C # - PullRequest
       145

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

82 голосов
/ 19 августа 2008

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

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

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

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

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

Ответы [ 14 ]

61 голосов
/ 19 августа 2008

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

using System.Linq;

IList<Foo> list = new List<Foo>();
IEnumerable<Foo> sortedEnum = list.OrderBy(f=>f.Bar);
IList<Foo> sortedList = sortedEnum.ToList();
55 голосов
/ 18 февраля 2011

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

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

54 голосов
/ 19 августа 2008

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

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

from c in list
orderby c.Engine
select c;

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

9 голосов
/ 19 августа 2008

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

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

4 голосов
/ 19 августа 2016

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

1 голос
/ 31 августа 2012
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));**
1 голос
/ 08 декабря 2010

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

    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();
            }
        }
    }
1 голос
/ 14 июля 2010

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

У меня есть два 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.

1 голос
/ 19 августа 2008

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

0 голосов
/ 11 сентября 2010

Это правильное решение?

        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

...