2008-08-19 41 views
68

Vì vậy, tôi đã gặp một vấn đề thú vị ngay hôm nay. Chúng tôi có một dịch vụ web WCF trả về một 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ó.Sắp xếp một IList trong C#

Hóa ra giao diện IList không có một phương pháp loại được xây dựng trong.

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

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

Vì vậy, câu hỏi của tôi là, không ai có một giải pháp thanh lịch để sắp xếp một IList

+0

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

Trả lời

51

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ó một IList<Car>, và chiếc xe có một tài sản Engine, tôi tin rằng bạn có thể sắp xếp như sau:

from c in list 
orderby c.Engine 
select c; 

Edit: Bạn cần phải được nhanh chóng để có được câu trả lời ở đây. Khi 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 tôi - tuy nhiên, các câu trả lời khác được trình bày đều có giá trị như nhau.

+0

Nó sẽ tạo ra một enumerable mới, có thể không được mong muốn trong một số kịch bản. Bạn không thể sắp xếp một ILI tại chỗ thông qua giao diện ngoại trừ bằng cách sử dụng phương pháp ArrayList.Adapter trong kiến ​​thức của tôi. –

9

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

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

2

Chuyển đổi của bạn IList vào 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ử System.Linq namespace (nó sẽ cung cấp loạt các phương pháp khuyến nông)

+6

'IList ' thực hiện 'IEnumerable ' và do đó không cần phải được chuyển đổi để sử dụng các thao tác LINQ. –

56

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

Dưới đây là một ví dụ sử dụng gõ mạnh. Không chắc chắn nếu nó nhất thiết phải là cách tốt nhất mặc dù.

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

Chức năng Truyền chỉ là triển khai lại phương pháp mở rộng đi kèm với 3,5 được viết như phương pháp tĩnh bình thường. Đó là khá xấu xí và tiết không may.

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ó tùy chọn để chọn cách khách hàng hủy tuần tự hóa danh sách được trả về từ dịch vụ.

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

0

Tìm thấy một bài viết tốt về điều này và nghĩ rằng tôi muốn chia sẻ. Check it out HERE

Về cơ bản.

Bạn có thể tạo các lớp sau và IComparer Lớp

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 Thanh toán trang web này để biết thêm thông tin ... Check it out HERE

0
using System.Linq; 

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

Đó là khá ghetto!.

1

Tìm thấy chủ đề này trong khi tôi đang tìm kiế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 được hoàn cảnh của tôi. Câu trả lời của Brody khá gần. Đây là tình huống và giải pháp tôi tìm thấy.

Tôi có hai ILists cùng loại được trả về bởi NHibernate và đã xuất hiện hai IList thành một, do đó cần phải phân loại.

Giống như Brody nói rằng tôi thực hiện một ICompare trên đối tượng (ReportFormat) là loại IList 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 sáp nhập vào một mảng cùng loại:

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

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

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

Kể từ mảng một chiều thực hiện các i nterface System.Collections.Generic.IList<T>, mảng có thể được sử dụng giống như IList gốc.

+0

đây là cách chính xác để thực hiện việc này. – user29964

0

Đây có phải là giải pháp hợp lệ không?

 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 Một C

Danh sách Một B C

IList lại Một B C

+0

Hợp lệ nếu nó thực sự là Danh sách . Trong một số trường hợp, bạn có các loại khác đang triển khai IList (ví dụ: một mảng đơn giản), nơi tệp không hoạt động sẽ không hoạt động. Quá tệ là phương thức Sort() không phải là phương thức mở rộng cho IList . – Cygon

1

hữu ích cho lưới điện phân loại danh sách sắp xếp phương thức này dựa trên tên thuộc tính. Làm theo 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(); 
      } 
     } 
    } 
49

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

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

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

Với những phần mở rộng, 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ó biết thêm trong bài: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

+0

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

+3

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 danh sách 'IList ' có thể được truyền tới giao diện' IList' không chung chung. Nếu bạn mã lớp của riêng bạn thực hiện giao diện 'IList ', hãy đảm bảo bạn cũng triển khai giao diện 'IList' không chung chung, hoặc mã sẽ không thành công với một ngoại lệ lớp đúc. – sstan

0
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));** 
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 phải xác định lớp ComparisonComparer<T> khi khuôn khổ đã 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 ra IComparison khi đang di chuyển.

Ngoài ra, nó hoạt động IList<T> đến IList 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, List<T> thực hiện IList được sử dụng phía sau hậu trường để thực hiện IList<T>, nhưng điều này không được bảo đảm và có thể dẫn đến mã giòn.

Cuối cùng, phương thức quá tải List<T>.Sort() có 4 chữ ký và chỉ 2 chữ ký được triển khai.

  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 giao diện 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]; 
     } 
    } 
} 

Cách 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 các chức năng của cơ bản List<T> để xử lý sắp xếp bất cứ khi nào có thể. Một lần nữa, hầu hết các triển khai IList<T> mà tôi đã thấy sử dụng. Trong trường hợp bộ sưu tập cơ bản là một kiểu khác, dự phòng để tạo một thể hiện mới là List<T> với các phần tử từ danh sách đầu vào, sử dụng nó để thực hiện 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ẽ làm việc ngay cả khi danh sách đầu vào không thực hiện giao diện IList.

Các vấn đề liên quan