Sắp xếp IList trong C #


86

Vì vậy, tôi đã gặp một vấn đề thú vị ngày hôm nay. Chúng tôi có dịch vụ web WCF trả về IList. Không thực sự là một vấn đề lớn cho đến khi tôi muốn sắp xếp nó.

Hóa ra giao diện IList không có sẵn một phương pháp sắp xếp.

Tôi đã kết thúc bằng cách sử dụng ArrayList.Adapter(list).Sort(new MyComparer())phương pháp để giải quyết vấn đề nhưng nó chỉ có vẻ hơi "ghetto" đối với tôi.

Tôi đã đùa với việc viết một phương thức mở rộng, cũng như kế thừa từ IList và triển khai phương thức Sort () của riêng tôi cũng như truyền đến một Danh sách nhưng không có phương thức nào trong số này có vẻ quá thanh lịch.

Vì vậy, câu hỏi của tôi là, có ai có giải pháp tốt để phân loại IList


Tại sao bạn lại trả lại một IList ngay từ đầu? Từ một dịch vụ WCF?
DaeMoohn

Câu trả lời:


54

Làm thế nào về việc sử dụng LINQ To Objects để sắp xếp cho bạn?

Giả sử bạn có IList<Car>, và chiếc xe có Enginetài sản, tôi tin rằng bạn có thể sắp xếp như sau:

from c in list
orderby c.Engine
select c;

Chỉnh sửa: Bạn cần phải nhanh chóng nhận được câu trả lời tại đây. Vì tôi đã trình bày một cú pháp hơi khác với các câu trả lời khác, tôi sẽ để lại câu trả lời của mình - tuy nhiên, các câu trả lời khác được trình bày đều có giá trị như nhau.


3
Nó sẽ tạo ra một danh sách mới, có thể không được mong muốn trong một số trường hợp. Bạn không thể sắp xếp IList <T> tại chỗ thông qua giao diện ngoại trừ bằng cách sử dụng phương thức ArrayList.Adapter theo hiểu biết của tôi.
Tanveer Badar

67

Bạn có thể sử dụng LINQ:

using System.Linq;

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

61

Câu hỏi này đã thôi thúc tôi viết một bài blog: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Tôi nghĩ rằng, lý tưởng nhất là .NET Framework sẽ bao gồm một phương pháp sắp xếp tĩnh chấp nhận một IList <T>, nhưng điều tốt nhất tiếp theo là tạo phương thức mở rộng của riêng bạn. Không quá khó để tạo một vài phương thức cho phép bạn sắp xếp một IList <T> như cách bạn làm với một Danh sách <T>. Phần thưởng là bạn có thể nạp chồng phương thức mở rộng LINQ OrderBy bằng cách sử dụng cùng một kỹ thuật, vì vậy cho dù bạn đang sử dụng List.Sort, IList.Sort hay IEnumerable.OrderBy, bạn đều có thể sử dụng cùng một cú pháp.

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

Với những phần mở rộng này, hãy sắp xếp IList của bạn giống như bạn làm một Danh sách:

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

Có thêm thông tin trong bài đăng: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/


1
Cách tiếp cận đúng thực sự sẽ là cung cấp một ISortableList<T>giao diện (với các phương pháp sắp xếp một phần của danh sách bằng cách sử dụng một số trình so sánh cụ thể), có List<T>triển khai nó và có một phương thức tĩnh có thể sắp xếp bất kỳ IList<T>bằng cách kiểm tra xem nó có được triển khai ISortableList<T>hay không và nếu không, sao chép nó vào một mảng, sắp xếp mảng đó, xóa IList<T>và thêm lại các mục.
supercat

4
Câu trả lời tuyệt vời! Tuy nhiên, một lời cảnh báo: cách tiếp cận này giả định rằng IList<T> listcó thể được truyền đến IListgiao diện không chung chung . Nếu bạn viết mã lớp riêng của mình triển khai IList<T>giao diện, hãy đảm bảo rằng bạn cũng triển khai IListgiao diện không chung chung , nếu không mã sẽ không thành công với ngoại lệ ép kiểu lớp.
sstan

1
@supercat: Những gì có thể ISortableList<T>cung cấp mà chưa có IList<T>? Hoặc, hỏi theo cách khác, tại sao không IList<T>thể sắp xếp tại chỗ mà không thêm lại các mục bằng phương thức tĩnh do bạn tưởng tượng?
HOẶC Người vẽ bản đồ

@ORMapper: Nếu danh sách sử dụng mảng làm kho dự phòng (phổ biến, nhưng không bắt buộc), thì quy trình sắp xếp truy cập trực tiếp các phần tử của mảng có thể nhanh hơn nhiều so với quy trình phải đi qua IList<T>giao diện để truy cập mọi phần tử. Sự khác biệt về tốc độ đủ lớn đến mức trong nhiều trường hợp, việc sao chép danh sách vào một mảng, sắp xếp mảng và sao chép lại danh sách có thể nhanh hơn so với việc cố gắng thực hiện quy trình sắp xếp danh sách tại chỗ.
supercat

1
Các ComparisonComparerlớp học là không cần thiết. Bạn có thể sử dụng phương pháp tĩnh tiêu chuẩn để Comparer<T>.Create(comparison)thay thế.
linepogl

9

Bạn sẽ phải làm một cái gì đó giống như tôi nghĩ (chuyển đổi nó thành một loại cụ thể hơn).

Có thể đưa nó vào Danh sách T hơn là ArrayList, để bạn có được sự an toàn về kiểu chữ và nhiều tùy chọn hơn về cách bạn triển khai trình so sánh.


4

Câu trả lời được chấp nhận bởi @DavidMills là khá tốt, nhưng tôi nghĩ nó có thể được cải thiện. Đối với một, không cần xác định ComparisonComparer<T>lớp khi khung công tác đã bao gồm một phương thức tĩnh Comparer<T>.Create(Comparison<T>). Phương pháp này có thể được sử dụng để tạo một IComparisoncách nhanh chóng.

Ngoài ra, nó phôi IList<T>để IListmà có khả năng gây nguy hiểm. Trong hầu hết các trường hợp mà tôi đã thấy, nông cụ List<T>nào IListđược sử dụng ở hậu trường để triển khaiIList<T> , nhưng điều này không được đảm bảo và có thể dẫn đến mã giòn.

Cuối cùng, List<T>.Sort()phương thức nạp chồng có 4 chữ ký và chỉ 2 trong số chúng được thực hiện.

  1. List<T>.Sort()
  2. List<T>.Sort(Comparison<T>)
  3. List<T>.Sort(IComparer<T>)
  4. List<T>.Sort(Int32, Int32, IComparer<T>)

Lớp dưới đây thực hiện tất cả 4 List<T>.Sort()chữ ký cho IList<T>giao diện:

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

Sử dụng:

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

Ý tưởng ở đây là tận dụng chức năng của phần mềm bên dưới List<T>để xử lý việc sắp xếp bất cứ khi nào có thể. Một lần nữa, hầu hết các IList<T>triển khai mà tôi đã thấy đều sử dụng điều này. Trong trường hợp khi tập hợp cơ bản là một kiểu khác, hãy dự phòng để tạo một phiên bản mới List<T>với các phần tử từ danh sách đầu vào, sử dụng nó để thực hiện việc sắp xếp, sau đó sao chép kết quả trở lại danh sách đầu vào. Điều này sẽ hoạt động ngay cả khi danh sách đầu vào không triển khai IListgiao diện.


2
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

Tìm thấy chủ đề này trong khi tôi đang tìm giải pháp cho vấn đề chính xác được mô tả trong bài đăng gốc. Tuy nhiên, không có câu trả lời nào đáp ứng hoàn toàn tình huống của tôi. Câu trả lời của Brody khá gần gũi. Đây là tình huống của tôi và giải pháp mà tôi đã tìm thấy.

Tôi có hai IList cùng loại được NHibernate trả lại và đã đưa hai IList thành một, do đó cần phải phân loại.

Giống như Brody đã nói, tôi đã triển khai ICompare trên đối tượng (ReportFormat) là loại IList của tôi:

 public class FormatCcdeSorter:IComparer<ReportFormat>
    {
       public int Compare(ReportFormat x, ReportFormat y)
        {
           return x.FormatCode.CompareTo(y.FormatCode);
        }
    }

Sau đó, tôi chuyển đổi IList đã hợp nhất thành một mảng cùng loại:

ReportFormat[] myReports = new ReportFormat[reports.Count]; //reports is the merged IList

Sau đó, sắp xếp mảng:

Array.Sort(myReports, new FormatCodeSorter());//sorting using custom comparer

Vì mảng một chiều thực hiện giao diện System.Collections.Generic.IList<T>, nên mảng có thể được sử dụng giống như IList ban đầu.


1

Hữu ích cho việc sắp xếp theo lưới Phương pháp này sắp xếp danh sách dựa trên tên thuộc tính. Như sau ví dụ.

    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

Đây là một ví dụ sử dụng cách gõ mạnh hơn. Tuy nhiên, không chắc đó có nhất thiết là cách tốt nhất hay không.

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

Hàm Cast chỉ là một bản tái hiện của phương thức mở rộng đi kèm với 3.5 được viết như một phương thức tĩnh thông thường. Nó khá xấu xí và dài dòng một cách đáng tiếc.


0

Trong VS2008, khi tôi nhấp vào tham chiếu dịch vụ và chọn "Định cấu hình tham chiếu dịch vụ", có một tùy chọn để chọn cách máy khách hủy tuần tự hóa danh sách trả về từ dịch vụ.

Đáng chú ý, tôi có thể chọn giữa System.Array, System.Collections.ArrayList và System.Collections.Generic.List


0
using System.Linq;

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

Đẹp quá!


0

Tìm thấy một bài viết tốt về điều này và nghĩ rằng tôi sẽ chia sẻ. Kiểm tra nó tại ĐÂY

Về cơ bản.

Bạn có thể tạo lớp sau và các lớp 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);
}
}

Sau đó, Nếu bạn có một IList, bạn có thể sắp xếp nó như thế này.

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

Nhưng Kiểm tra trang web này để biết thêm thông tin ... Kiểm tra nó TẠI ĐÂY


0

Đây có phải là một giải pháp hợp lệ?

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

Kết quả là: IList B A C

Liệt kê A B C

IList lại A B C


1
Hợp lệ nếu nó thực sự là một Danh sách <T>. Trong một số trường hợp, bạn có các kiểu triển khai IList <T> khác (ví dụ: một mảng thuần túy) nơi downcast sẽ không hoạt động. Thật tệ khi phương thức Sort () không phải là một phương thức mở rộng cho IList <T>.
Cygon

0

Điều này trông ĐƠN GIẢN HƠN nếu bạn hỏi tôi. Điều này hoạt động HOÀN HẢO đối với tôi.

Bạn có thể sử dụng Cast () để thay đổi nó thành IList, sau đó sử dụng OrderBy ():

    var ordered = theIList.Cast<T>().OrderBy(e => e);

WHERE T là kiểu ví dụ. Model.E Employee hoặc Plugin.ContactService.Shared.Contact

Sau đó, bạn có thể sử dụng vòng lặp for và DONE của nó.

  ObservableCollection<Plugin.ContactService.Shared.Contact> ContactItems= new ObservableCollection<Contact>();

    foreach (var item in ordered)
    {
       ContactItems.Add(item);
    }

-1

Chuyển đổi của bạn IListthành List<T>hoặc một số bộ sưu tập chung khác và sau đó bạn có thể dễ dàng truy vấn / sắp xếp nó bằng cách sử dụng System.Linqkhông gian tên (nó sẽ cung cấp một loạt các phương thức mở rộng)


9
IList<T>dụng cụ IEnumerable<T>và do đó không cần phải được chuyển đổi sang sử dụng các hoạt động LINQ.
Steve Guidi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.