C #에서 IList 정렬


87

그래서 오늘 흥미로운 문제를 발견했습니다. IList를 반환하는 WCF 웹 서비스가 있습니다. 내가 그것을 분류하고 싶을 때까지별로 큰 문제는 아닙니다.

IList 인터페이스에는 정렬 메서드가 내장되어 있지 않습니다.

나는 결국 ArrayList.Adapter(list).Sort(new MyComparer())문제를 해결하기 위해 방법을 사용 했지만 그것은 나에게 약간 "빈민가"처럼 보였다.

나는 확장 메서드를 작성하고 IList에서 상속하고 자체 Sort () 메서드를 구현하고 List로 캐스팅하는 데 놀랐지 만 이들 중 어느 것도 지나치게 우아하게 보이지 않았습니다.

그래서 내 질문은 누구든지 IList 정렬에 대한 우아한 해결책이 있습니까?


처음에 IList를 반환하는 이유는 무엇입니까? WCF 서비스에서?
DaeMoohn

답변:


55

LINQ To Objects를 사용하여 정렬하는 것은 어떻습니까?

당신이 가지고 있고 IList<Car>차에 Engine재산이 있다고 가정하면 다음과 같이 분류 할 수 있다고 생각합니다.

from c in list
orderby c.Engine
select c;

편집 : 여기에서 답을 얻으려면 빨리해야합니다. 다른 답변과 약간 다른 구문을 제시 했으므로 답변을 남기겠습니다. 그러나 제시된 다른 답변은 똑같이 유효합니다.


3
일부 시나리오에서는 바람직하지 않을 수있는 새 열거 형을 생성합니다. 내가 아는 한 ArrayList.Adapter 메서드를 사용하는 경우를 제외하고는 인터페이스를 통해 IList <T>를 제자리에서 정렬 할 수 없습니다.
Tanveer Badar 2014-08-28

68

LINQ를 사용할 수 있습니다.

using System.Linq;

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

61

이 질문에 영감을 받아 블로그 게시물을 작성했습니다. http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

이상적으로는 .NET Framework에 IList <T>를 허용하는 정적 정렬 메서드가 포함되어 있다고 생각하지만 다음으로 좋은 방법은 사용자 고유의 확장 메서드를 만드는 것입니다. List <T>처럼 IList <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/


1
올바른 접근 방식은 ISortableList<T>인터페이스 (특정 비교자를 사용하여 목록의 일부를 정렬하는 List<T>메서드 포함)를 제공 하고, IList<T>구현 ISortableList<T>하고, 구현 여부를 확인하여 정렬 할 수있는 정적 메서드를 갖는 것입니다 . 배열에 복사하고 정렬하고을 지우고 IList<T>항목을 다시 추가합니다.
supercat

4
멋진 대답! 그러나주의 할 점 :이 접근 방식은를 IList<T> list일반 IList인터페이스 가 아닌 인터페이스 로 캐스팅 할 수 있다고 가정합니다 . IList<T>인터페이스를 구현하는 고유 한 클래스를 코딩하는 경우 제네릭이 아닌 IList인터페이스 도 구현해야합니다 . 그렇지 않으면 코드가 클래스 캐스트 예외와 함께 실패합니다.
sstan

1
@supercat : ISortableList<T>아직없는 것이 무엇을 제공 할 수 IList<T>있습니까? 또는 다르게 질문 IList<T>했습니다. 상상 한 정적 방법으로 항목을 다시 추가하지 않고 제자리에서 정렬 할 수없는 이유는 무엇입니까?
OR Mapper

@ORMapper : 목록이 배열을 백업 저장소로 사용하는 경우 (공통이지만 필수는 아님) 배열 요소에 직접 액세스하는 정렬 루틴이 IList<T>모든 요소에 액세스하기 위해 인터페이스를 거쳐야하는 루틴보다 훨씬 빠를 수 있습니다 . 속도 차이는 많은 경우에 목록을 정렬 루틴으로 처리하는 것보다 배열에 목록을 복사하고, 배열을 정렬하고, 목록을 다시 복사하는 것이 더 빠를 수있을만큼 충분히 크다.
supercat nov.

1
ComparisonComparer클래스는 필요하지 않습니다. Comparer<T>.Create(comparison)대신 표준 정적 방법을 사용할 수 있습니다 .
linepogl

9

당신은 내가 생각하는 것과 같은 것을해야 할 것입니다 (더 구체적인 유형으로 변환하십시오).

ArrayList가 아닌 List of T로 가져 가면 유형 안전성과 비교자를 구현하는 방법에 대한 더 많은 옵션을 얻을 수 있습니다.


4

@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>)

아래 클래스 List<T>.Sort()IList<T>인터페이스에 대한 4 개의 서명을 모두 구현합니다 .

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인터페이스를 구현하지 않는 경우에도 작동합니다 .


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

ASC 대신 DESC를 원하면 어떻게해야합니까?
sam byte

1

원래 게시물에 설명 된 정확한 문제에 대한 해결책을 찾고있는 동안이 스레드를 찾았습니다. 그러나 대답 중 어느 것도 내 상황을 완전히 충족시키지 못했습니다. Brody의 대답은 매우 가깝습니다. 여기에 내 상황과 해결책이 있습니다.

나는 NHibernate에 의해 반환 된 동일한 유형의 두 개의 IList를 가지고 있으며 두 개의 IList를 하나로 이머 지했기 때문에 정렬이 필요합니다.

Brody가 말했듯이 IList의 유형 인 개체 (ReportFormat)에 ICompare를 구현했습니다.

 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

1 차원 배열이 인터페이스를 구현 System.Collections.Generic.IList<T>하므로 원래 IList처럼 배열을 사용할 수 있습니다.


1

그리드 정렬에 유용합니다.이 메서드는 속성 이름을 기준으로 목록을 정렬합니다. 예를 따르십시오.

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

0

다음은 강력한 타이핑을 사용한 예입니다. 그래도 반드시 최선의 방법인지 확실하지 않습니다.

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와 함께 제공되는 확장 메서드를 다시 구현 한 것입니다. 불행히도 매우 추하고 장황합니다.


0

VS2008에서는 서비스 참조를 클릭하고 "서비스 참조 구성"을 선택하면 클라이언트가 서비스에서 반환 된 목록을 역 직렬화하는 방법을 선택할 수있는 옵션이 있습니다.

특히 System.Array, System.Collections.ArrayList 및 System.Collections.Generic.List 중에서 선택할 수 있습니다.


0
using System.Linq;

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

꽤 예쁘다! 게토.


0

이것에 대한 좋은 게시물을 발견하고 공유 할 것이라고 생각했습니다. 여기에서 확인하세요

원래.

다음 클래스와 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());

그러나 자세한 내용은이 사이트를 확인하십시오. 여기에서 확인하십시오.


0

이것이 유효한 솔루션입니까?

        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

IList 다시 A B C


1
실제로 List <T> 인 경우 유효합니다. 어떤 경우에는 다운 캐스트가 작동하지 않는 IList <T> (예 : 일반 배열)를 구현하는 다른 유형이 있습니다. Sort () 메서드가 IList <T>에 대한 확장 메서드가 아니라는 점이 너무 나쁩니다.
Cygon

0

당신이 저에게 물어 보면 이것은 훨씬 더 간단 해 보입니다. 이것은 나를 위해 완벽하게 작동합니다.

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

-1

다른 일반 컬렉션 IList으로 변환 List<T>한 다음 System.Linq네임 스페이스를 사용하여 쉽게 쿼리 / 정렬 할 수 있습니다 (확장 메서드를 제공합니다).


9
IList<T>구현 IEnumerable<T>그러므로와는 Linq에 작업을 사용하도록 변환 할 필요가 없습니다.
Steve Guidi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.