Sortieren einer IList in C #


86

So bin ich heute auf ein interessantes Problem gestoßen. Wir haben einen WCF-Webdienst, der eine IList zurückgibt. Nicht wirklich eine große Sache, bis ich es sortieren wollte.

Es stellt sich heraus, dass in der IList-Schnittstelle keine Sortiermethode integriert ist.

Am Ende benutzte ich die ArrayList.Adapter(list).Sort(new MyComparer())Methode, um das Problem zu lösen, aber es schien mir nur ein bisschen "Ghetto" zu sein.

Ich spielte mit dem Schreiben einer Erweiterungsmethode, auch mit dem Erben von IList und dem Implementieren meiner eigenen Sort () -Methode sowie dem Casting in eine Liste, aber keine davon schien übermäßig elegant.

Meine Frage ist also, ob jemand eine elegante Lösung zum Sortieren einer IList hat


Warum würden Sie überhaupt eine IList zurückgeben? Von einem WCF-Dienst?
DaeMoohn

Antworten:


54

Wie wäre es mit LINQ To Objects, um für Sie zu sortieren?

Angenommen, Sie haben eine IList<Car>, und das Auto hatte eine EngineEigenschaft, ich glaube, Sie könnten wie folgt sortieren:

from c in list
orderby c.Engine
select c;

Bearbeiten: Sie müssen schnell sein, um hier Antworten zu erhalten. Da ich eine etwas andere Syntax als die anderen Antworten angegeben habe, werde ich meine Antwort belassen. Die anderen Antworten sind jedoch gleichermaßen gültig.


2
Es wird eine neue Aufzählung erstellt, was in einigen Szenarien möglicherweise nicht wünschenswert ist. Sie können eine IList <T> nicht direkt über die Schnittstelle sortieren, außer nach meinem Wissen mithilfe der ArrayList.Adapter-Methode.
Tanveer Badar

66

Sie können LINQ verwenden:

using System.Linq;

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

61

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

Ich denke, dass .NET Framework im Idealfall eine statische Sortiermethode enthält, die eine IList <T> akzeptiert, aber das nächstbeste ist, eine eigene Erweiterungsmethode zu erstellen. Es ist nicht allzu schwierig, einige 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 derselben Technik überladen, sodass Sie unabhängig von List.Sort, IList.Sort oder IEnumerable.OrderBy genau dieselbe 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));
    }

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

Sortieren Sie mit diesen Erweiterungen Ihre IList wie 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));

Weitere Informationen finden Sie im Beitrag: http://blog.velir.com/index.php/2011/02/17/ilistt-sorting-a-better-way/


1
Der richtige Ansatz wäre wirklich gewesen, eine ISortableList<T>Schnittstelle anzubieten (mit Methoden zum Sortieren eines Teils der Liste unter Verwendung eines bestimmten Vergleichers), sie zu List<T>implementieren und eine statische Methode zu haben, die jede sortieren könnte, IList<T>indem geprüft wird, ob sie implementiert ist, ISortableList<T>und wenn nicht, Kopieren Sie es in ein Array, sortieren Sie es, löschen Sie es IList<T>und fügen Sie die Elemente erneut hinzu.
Supercat

4
Wunderbare Antwort! Ein Wort der Vorsicht: Bei diesem Ansatz wird davon ausgegangen, dass das IList<T> listauf die nicht generische IListSchnittstelle übertragen werden kann. Wenn Sie Ihre eigene Klasse codieren, die die IList<T>Schnittstelle implementiert , stellen Sie sicher, dass Sie auch die nicht generische IListSchnittstelle implementieren. Andernfalls schlägt der Code mit einer Klassenumwandlungsausnahme fehl.
Sstan

1
@supercat: Was könnte das ISortableList<T>bieten, was noch nicht drin ist IList<T>? Oder anders gefragt, warum kann man nicht IList<T>vor Ort sortieren, ohne die Elemente nach Ihrer imaginären statischen Methode erneut hinzuzufügen?
ODER Mapper

@ORMapper: Wenn eine Liste ein Array als Sicherungsspeicher verwendet (häufig, aber nicht erforderlich), kann eine Sortierroutine, die direkt auf Array-Elemente zugreift, viel schneller sein als eine, die die IList<T>Schnittstelle durchlaufen muss, um auf jedes Element zuzugreifen. Der Geschwindigkeitsunterschied ist so groß, dass es in vielen Fällen schneller sein kann, eine Liste in ein Array zu kopieren, das Array zu sortieren und die Liste zurück zu kopieren, als zu versuchen, die Liste von einer Sortierroutine verarbeiten zu lassen.
Supercat

1
Die ComparisonComparerKlasse ist nicht notwendig. Sie können Comparer<T>.Create(comparison)stattdessen die statische Standardmethode verwenden.
Linepogl

9

Du wirst so etwas tun müssen, denke ich (es in einen konkreteren Typ umwandeln).

Nehmen Sie es vielleicht in eine Liste von T und nicht in eine ArrayList, damit Sie Typensicherheit und mehr Optionen für die Implementierung des Vergleichers erhalten.


4

Die akzeptierte Antwort von @DavidMills ist ziemlich gut, aber ich denke, sie kann verbessert werden. Zum einen muss die ComparisonComparer<T>Klasse nicht definiert werden , wenn das Framework bereits eine statische Methode enthält Comparer<T>.Create(Comparison<T>). Mit dieser Methode können Sie eine erstellenIComparison on-the-fly .

Außerdem wirft es, IList<T>für IListdie das Potenzial besteht, gefährlich zu sein. In den meisten Fällen, die ich gesehen habe, wird List<T>das , was implementiert IListwird, hinter den Kulissen verwendet, um zu implementierenIList<T> , aber dies ist nicht garantiert und kann zu sprödem Code führen.

Schließlich hat die überladene List<T>.Sort()Methode 4 Signaturen und nur 2 davon 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 folgende Klasse implementiert alle 4 List<T>.Sort()Signaturen für die IList<T>Schnittstelle:

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

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

Hier geht es darum, die Funktionalität des Basiswerts List<T>zu nutzen, um die Sortierung nach Möglichkeit durchzuführen. Wiederum verwenden die meisten IList<T>Implementierungen, die ich gesehen habe, dies. Wenn es sich bei der zugrunde liegenden Auflistung um einen anderen Typ handelt, greifen Sie auf das Erstellen einer neuen Instanz List<T>mit Elementen aus der Eingabeliste zurück, verwenden Sie diese zum Sortieren und kopieren Sie die Ergebnisse zurück in die Eingabeliste. Dies funktioniert auch dann, wenn die Eingabeliste die IListSchnittstelle nicht implementiert .


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

Ich habe diesen Thread gefunden, als ich nach einer Lösung für das genaue Problem gesucht habe, das im ursprünglichen Beitrag beschrieben wurde. Keine der Antworten entsprach jedoch vollständig meiner Situation. Brodys Antwort war ziemlich nah. Hier ist meine Situation und Lösung, die ich gefunden habe.

Ich habe zwei IListen desselben Typs, die von NHibernate zurückgegeben wurden, und habe die beiden IListen zu einer zusammengefasst, weshalb eine Sortierung erforderlich ist.

Wie Brody sagte, habe ich einen ICompare für das Objekt (ReportFormat) implementiert, der der Typ meiner IList ist:

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

Ich konvertiere dann die zusammengeführte IList in ein Array des gleichen Typs:

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

Sortieren Sie dann das Array:

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

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


1

Nützlich für die Rastersortierung. Diese Methode sortiert die Liste nach Eigenschaftsnamen. Wie folgt das 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();
            }
        }
    }

0

Hier ist ein Beispiel mit der stärkeren Eingabe. Ich bin mir nicht sicher, ob es unbedingt 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 Neuimplementierung der Erweiterungsmethode, die mit 3.5 als normale statische Methode geschrieben wurde. Es ist leider ziemlich hässlich und wortreich.


0

Wenn ich in VS2008 auf die Dienstreferenz klicke und "Dienstreferenz konfigurieren" auswähle, kann optional festgelegt werden, wie der Client vom Dienst zurückgegebene Listen de-serialisiert.

Insbesondere kann ich zwischen System.Array, System.Collections.ArrayList und System.Collections.Generic.List wählen


0
using System.Linq;

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

Das ist hübsch! Ghetto.


0

Ich habe einen guten Beitrag dazu gefunden und dachte, ich würde ihn teilen. Schau es dir HIER an

Grundsätzlich.

Sie können die folgenden Klassen- und IComparer-Klassen erstellen

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

Wenn Sie eine IList haben, können Sie diese so sortieren.

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

Weitere Informationen finden Sie auf dieser Website. HIER


0

Ist das 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.


1
Gültig, wenn es sich wirklich um eine Liste <T> handelt. In einigen Fällen haben Sie andere Typen, die IList <T> implementieren (z. B. ein einfaches Array), bei denen der Downcast nicht funktioniert. Schade, dass die Sort () -Methode keine Erweiterungsmethode für IList <T> ist.
Cygon

0

Das sieht viel einfacher aus, wenn Sie mich fragen. Das funktioniert perfekt für mich.

Sie können Cast () verwenden, um es in IList zu ändern, und dann OrderBy () verwenden:

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

WO T ist der Typ zB. Model.Employee oder Plugin.ContactService.Shared.Contact

Dann können Sie eine for-Schleife verwenden und fertig.

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

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

-1

Konvertieren Sie Ihre Sammlung IListin eine List<T>oder eine andere generische Sammlung, und Sie können sie dann einfach mithilfe des System.LinqNamespace abfragen / sortieren (sie bietet eine Reihe von Erweiterungsmethoden).


9
IList<T>implementiert IEnumerable<T>und muss daher nicht konvertiert werden, um Linq-Operationen zu verwenden.
Steve Guidi
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.