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

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

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

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

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

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

Ответ 1

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

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

from c in list
orderby c.Engine
select c;

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

Ответ 2

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

using System.Linq;

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

Ответ 3

Этот вопрос вдохновил меня написать сообщение в блоге: 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));
    }

    // 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/

Ответ 4

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

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

Ответ 5

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

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

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

  • List<T>.Sort()
  • List<T>.Sort(Comparison<T>)
  • List<T>.Sort(IComparer<T>)
  • 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.

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

В VS2008, когда я нажимаю ссылку на службу и выбираю "Configure Service Reference", есть возможность выбрать, как клиент де-сериализует списки, возвращенные из службы.

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

Ответ 11

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

В принципе.

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

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

Ответ 12

using System.Linq;

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

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

Ответ 13

Является ли это допустимым решением?

        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 В С

Список В С

Иллис снова В С

Ответ 14

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