2008-08-19 6 views
68

So stieß ich heute auf ein interessantes Problem. Wir haben einen WCF-Webdienst, der eine IList zurückgibt. Nicht wirklich eine große Sache, bis ich es sortieren wollte.Sortieren einer IList in C#

Schaltet die IList-Schnittstelle aus kein Sortiermethode eingebaut hat.

Ich landete mit dem ArrayList.Adapter(list).Sort(new MyComparer()) Methode das Problem zu lösen, aber es schien nur ein bisschen „Ghetto“ zu mir.

Ich spielte mit dem Schreiben einer Extension-Methode, auch mit der Übernahme von IList und die Implementierung meiner eigenen Sort() -Methode sowie Casting zu einer Liste, aber keiner von diesen schien übermäßig elegant.

Also meine Frage ist, hat jemand eine elegante Lösung, die ein IList

+0

Warum würden Sie in erster Linie eine IList zurückgeben? Von einem WCF-Dienst? – DaeMoohn

Antwort

51

Wie wäre es mit LINQ To Objects (es wird Bündel von Erweiterungsmethoden liefern) für Sie zu sortieren?

Sagen Sie bitte eine IList<Car> haben, und das Auto hatte eine Engine Eigenschaft, glaube ich Ihnen wie folgt sortiert werden könnte:

from c in list 
orderby c.Engine 
select c; 

Edit: Sie müssen schnell sein, Antworten hier. Da ich den anderen Antworten eine etwas andere Syntax gegeben habe, überlasse ich meine Antwort - die anderen Antworten sind jedoch gleichermaßen gültig.

+0

Es wird ein neues Enumerable erstellt, das in einigen Szenarien möglicherweise nicht wünschenswert ist. Sie können eine IList nicht direkt über die Schnittstelle sortieren, außer mit der ArrayList.Adapter-Methode. –

9

Sortierung Sie gehen zu müssen, so etwas wie zu tun, dass ich denke, (wandelt es in einen konkreteren Typen).

Vielleicht nehmen Sie es in eine Liste von T statt ArrayList, so dass Sie Typ Sicherheit und mehr Optionen erhalten, wie Sie den Vergleich implementieren.

2

Konvertieren Sie Ihre IList in List<T> oder eine andere generische Sammlung und dann können Sie leicht abfragen/Art es System.Linq Namespace

+6

'IList ' implementiert 'IEnumerable ' und muss daher nicht für die Verwendung von Linq-Operationen konvertiert werden. –

56

können Sie LINQ verwenden:

using System.Linq; 

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

Hier ist ein Beispiel die stärkere Typisierung verwenden. Nicht sicher, ob es notwendigerweise der beste Weg ist.

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

die CAST-Funktion ist nur eine Reimplementation des Verlängerungsverfahren, das als eine normale statische Methode geschrieben mit 3,5 kommt. Es ist leider ziemlich hässlich und ausführlich.

0

In VS2008, wenn ich auf die Service-Referenz klicke und "Service Reference" auswähle, gibt es eine Option zu wählen, wie der Client vom Service zurückgegebene Listen demerialisiert.

Bemerkenswert ist, kann ich zwischen System.Array, System.Collections.ArrayList und System.Collections.Generic.List

0

Gefunden einen guten Beitrag zu diesem Thema wählen und dachte, ich würde Aktie. Check it out HERE

Grundsätzlich.

Sie können die folgende Klasse und IComparer Klassen

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

Dann erstellen Wenn Sie eine IList haben, können Sie es wie folgt sortiert werden.

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

Aber Kasse diese Seite für mehr Informationen ... Check it out HERE

0
using System.Linq; 

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

, das ziemlich ist! Ghetto.

1

Dieser Thread gefunden, während ich nach einer Lösung für das genaue Problem im ursprünglichen Beitrag beschrieben suchte. Keine der Antworten erfüllte meine Situation jedoch vollständig. Brody's Antwort war ziemlich knapp. Hier ist meine Situation und Lösung, die ich gefunden habe.

Ich habe zwei ILists des gleichen Typs von NHibernate zurückgegeben und haben die zwei IList in eins, daher die Notwendigkeit zum Sortieren entstanden.

Wie Brody sagte ich ein ICompare auf dem Objekt (ReportFormat) implementiert, die die Art meines IList ist:

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

ich dann das fusionierte IList auf ein Array vom gleichen Typ konvertieren:

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

dann das Array sortieren:

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

Da eindimensionales Array implementiert die i nterface System.Collections.Generic.IList<T> kann das Array genau wie das ursprüngliche IList verwendet werden.

+0

Dies ist der richtige Weg, dies zu tun. – user29964

0

Ist dies eine gültige Lösung?

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

Das Ergebnis war: IList B A C

Liste A B C

IList wieder A B C

+0

Gültig, wenn es wirklich eine Liste ist. In einigen Fällen haben Sie andere Typen, die IList implementieren (z. B. ein einfaches Array), in dem der Downcast nicht funktioniert. Schade, dass die Sort() -Methode keine Erweiterungsmethode für IList ist. – Cygon

1

Nützlich für grid Sortier Diese Methode sortiert die Liste basierend auf Eigenschaftsnamen. Folgen Sie dem Beispiel.

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

Diese Frage hat mich inspiriert eine Blog-Post zu schreiben: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

Ich denke, dass im Idealfall würde das .NET Framework eine statischen Sortierverfahren umfasst, die ein IList <T> akzeptiert, aber die nächste beste Ding ist, deine eigene Verlängerungsmethode zu verursachen. Es ist nicht schwer, ein paar Methoden zu erstellen, mit denen Sie eine IList <T> wie eine Liste <T> sortieren können. Als Bonus können Sie die LINQ OrderBy-Erweiterungsmethode mit der gleichen Technik überladen, so dass Sie, ob Sie List.Sort, IList.Sort oder IEnumerable.OrderBy verwenden, die exakt gleiche Syntax verwenden können.

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

Mit diesen Erweiterungen sortieren Sie Ihre IList wie würden Sie eine Liste:

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

Dort in der Post weitere Informationen ist: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/

+0

Der richtige Ansatz wäre wirklich eine 'ISortableList ' Schnittstelle (mit Methoden, um einen Teil der Liste mit einem bestimmten Vergleicher zu sortieren), haben' List 'implementieren, und haben eine statische Methode, die eine beliebige' IList 'durch Überprüfung, ob es' ISortableList 'implementiert und, wenn nicht, kopieren Sie es in ein Array, sortiere das, lösche die 'IList ', und die Elemente erneut hinzufügen. – supercat

+3

Wunderbare Antwort! Allerdings ein Wort der Vorsicht: Dieser Ansatz geht davon aus, dass die "IList Liste" in die nicht-generische "IList" -Schnittstelle umgewandelt werden kann. Wenn Sie Ihre eigene Klasse codieren, die die IList Schnittstelle implementiert, stellen Sie sicher, dass Sie auch die nicht generische IList-Schnittstelle implementieren, oder der Code wird mit einer Klassenausnahme fehlschlagen. – 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

Die akzeptierte Antwort von @DavidMills ist ziemlich gut, aber ich denke, dass es verbessert werden kann. Zum einen muss die Klasse ComparisonComparer<T> nicht definiert werden, wenn das Framework bereits eine statische Methode Comparer<T>.Create(Comparison<T>) enthält. Diese Methode kann verwendet werden, um eine IComparison on the fly zu erstellen.

Auch wirft es IList<T> zu IList, die potenziell gefährlich sein kann. In den meisten Fällen, die ich gesehen habe, List<T>, die implementiert IList wird hinter den Kulissen verwendet, um IList<T> zu implementieren, aber das ist nicht garantiert und kann zu spröden Code führen.

Schließlich hat die überladene List<T>.Sort() Methode 4 Signaturen und nur 2 von ihnen sind implementiert.

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

Die unten Klasse implementiert alle 4 List<T>.Sort() Unterschriften für die IList<T> Schnittstelle:

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

:

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

Die Idee dabei ist, die Funktionalität der zugrunde liegenden List<T> zu nutzen, wann immer möglich zu handhaben zu sortieren. Die meisten Implementierungen von IList<T>, die ich gesehen habe, verwenden dies. Wenn die zugrunde liegende Sammlung ein anderer Typ ist, erstellen Sie eine neue Instanz von List<T> mit Elementen aus der Eingabeliste, verwenden Sie sie, um die Sortierung durchzuführen, und kopieren Sie dann die Ergebnisse zurück in die Eingabeliste. Dies funktioniert auch dann, wenn die Eingabeliste die Schnittstelle IList nicht implementiert.