564 Stimmen

Liste<T> oder IList<T>

Kann jemand mir erklären, warum ich IList über Liste in C# verwenden möchten?

Verwandte Frage: Warum wird es als schlecht angesehen, etwas zu enthüllen List<T>

2 Stimmen

Internet-Suche nach "Code zu einer Schnittstelle nicht eine Implementierung" und Sie können den ganzen Tag lesen..und finden Sie ein paar h0ly Kriege.

11 Stimmen

Diese Frage ist nicht meinungsbasiert. List<T> , IList<T> y T[] sind jeweils für verschiedene Szenarien besser geeignet, und der Unterschied ist im Allgemeinen ziemlich objektiv. Es ist wie diese andere Frage . Die andere Frage weist viele Gemeinsamkeiten auf, ist aber wohl kein echtes Duplikat. In beiden Fällen handelt es sich jedoch nicht um eine Meinungsäußerung. Möglicherweise hat der Prüfer nur auf den Titel der Frage geschaut, ohne die Frage selbst zu lesen. Der Körper ist objektiv.

494voto

tvanfosson Punkte 506878

Wenn Sie Ihre Klasse in einer Bibliothek zur Verfügung stellen, die auch von anderen verwendet wird, sollten Sie sie im Allgemeinen über Schnittstellen und nicht über konkrete Implementierungen zur Verfügung stellen. Dies ist hilfreich, wenn Sie die Implementierung Ihrer Klasse später ändern wollen, um eine andere konkrete Klasse zu verwenden. In diesem Fall müssen die Benutzer Ihrer Bibliothek ihren Code nicht aktualisieren, da sich die Schnittstelle nicht ändert.

Wenn Sie es nur intern verwenden, ist das vielleicht nicht so wichtig, und die Verwendung von List<T> kann in Ordnung sein.

20 Stimmen

Ich verstehe den (feinen) Unterschied nicht. Gibt es ein Beispiel, auf das Sie mich hinweisen könnten?

153 Stimmen

Angenommen, Sie hatten ursprünglich List<T> verwendet und wollten nun eine spezialisierte CaseInsensitiveList<T> verwenden, die beide IList<T> implementieren. Wenn Sie den konkreten Typ verwenden, müssen alle Aufrufer aktualisiert werden. Bei IList<T> muss der Aufrufer nicht geändert werden.

58 Stimmen

Ah. JA, OKAY. Ich verstehe das. Den kleinsten gemeinsamen Nenner für einen Vertrag zu wählen. Vielen Dank!

361voto

Arec Barrwin Punkte 59801

Die weniger populäre Antwort ist, dass Programmierer gerne so tun, als ob ihre Software auf der ganzen Welt wiederverwendet wird, obwohl die meisten Projekte nur von einer kleinen Anzahl von Personen gewartet werden, und egal, wie schön schnittstellenbezogene Soundbites sind, man macht sich etwas vor.

Architektur Astronauten . Die Wahrscheinlichkeit, dass Sie jemals eine eigene IList schreiben werden, die den bereits im .NET-Framework vorhandenen ILists etwas hinzufügt, ist so gering, dass es sich um theoretische Gummibärchen handelt, die für "Best Practices" reserviert sind.

Software astronauts

Wenn Sie in einem Vorstellungsgespräch gefragt werden, welche Sprache Sie verwenden, sagen Sie natürlich IList, lächeln und freuen sich, dass Sie so clever sind. Oder für eine öffentlich zugängliche API, IList. Ich hoffe, Sie verstehen, worauf ich hinaus will.

71 Stimmen

Ich muss Ihrer sardonischen Antwort widersprechen. Ich widerspreche nicht völlig der Meinung, dass Überarchitektur ein echtes Problem ist. Ich denke jedoch, dass insbesondere bei Sammlungen die Schnittstellen wirklich glänzen. Angenommen, ich habe eine Funktion, die Folgendes zurückgibt IEnumerable<string> kann ich innerhalb der Funktion eine List<string> für einen internen Sicherungsspeicher, um meine Sammlung zu generieren, aber ich möchte nur, dass Anrufer den Inhalt aufzählen, nicht hinzufügen oder entfernen. Die Annahme einer Schnittstelle als Parameter vermittelt eine ähnliche Botschaft: "Ich brauche eine Sammlung von Strings, aber keine Sorge, ich werde sie nicht ändern."

46 Stimmen

Bei der Softwareentwicklung geht es um die Umsetzung von Absichten. Wenn Sie der Meinung sind, dass Schnittstellen nur für den Aufbau überdimensionierter, grandioser Architekturen nützlich sind und in kleinen Geschäften nichts zu suchen haben, dann hoffe ich, dass die Person, die Ihnen im Vorstellungsgespräch gegenübersitzt, nicht ich bin.

1 Stimmen

Die Verwendung von IList anstelle von List führt nicht einmal zusätzliche Wörter in den Code ein, so dass ich das Argument, dies führe zu unnötigem Code, kaum glauben kann.

196voto

Rinat Abdullin Punkte 22138

Schnittstelle ist ein Versprechen (oder einen Vertrag).

Wie immer bei den Verheißungen - Je kleiner, desto besser .

65 Stimmen

Nicht immer besser, nur einfacher zu halten! :)

7 Stimmen

Ich verstehe das nicht. Also IList ist das Versprechen. Welches ist hier das kleinere und bessere Versprechen? Das ist nicht logisch.

4 Stimmen

Bei jeder anderen Klasse würde ich zustimmen. Aber nicht für List<T> denn AddRange nicht auf der Schnittstelle definiert ist.

131voto

perfectionist Punkte 4026

Manche Leute sagen: "Benutze immer IList<T> anstelle von List<T> ".
Sie wollen, dass Sie Ihre Methodensignaturen ändern von void Foo(List<T> input) zu void Foo(IList<T> input) .

Diese Leute liegen falsch.

Es ist vielschichtiger als das. Wenn Sie eine IList<T> als Teil der öffentlichen Schnittstelle zu Ihrer Bibliothek, lassen Sie sich interessante Optionen, um vielleicht in Zukunft eine benutzerdefinierte Liste zu erstellen. Vielleicht werden Sie diese Option nie brauchen, aber es ist ein Argument. Ich denke, es ist das gesamte Argument für die Rückgabe der Schnittstelle anstelle des konkreten Typs. Es ist erwähnenswert, aber in diesem Fall hat es einen gravierenden Fehler.

Als kleines Gegenargument kann man anführen, dass jeder einzelne Anrufer eine List<T> und der aufrufende Code ist übersät mit .ToList()

Aber viel wichtiger ist, wenn Sie eine IList als Parameter akzeptieren, sollten Sie vorsichtig sein, denn IList<T> y List<T> verhalten sich nicht auf die gleiche Weise. Trotz der Ähnlichkeit im Namen und trotz der gemeinsamen Schnittstelle sie tun no dieselbe aussetzen Vertrag .

Angenommen, Sie haben diese Methode:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

Ein hilfsbereiter Kollege "refaktorisiert" die Methode so, dass sie Folgendes akzeptiert IList<int> .

Ihr Code ist jetzt fehlerhaft, weil int[] implementiert IList<int> aber eine feste Größe hat. Der Vertrag für ICollection<T> (die Basis von IList<T> ) erfordert, dass der Code, der es verwendet, die IsReadOnly bevor Sie versuchen, Objekte der Sammlung hinzuzufügen oder zu entfernen. Der Vertrag für List<T> nicht.

Das Liskovsche Substitutionsprinzip (vereinfacht) besagt, dass ein abgeleiteter Typ anstelle eines Basistyps ohne zusätzliche Vor- oder Nachbedingungen verwendet werden können sollte.

Dies scheint gegen das Liskovsche Substitutionsprinzip zu verstoßen.

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

Aber das ist nicht der Fall. Die Antwort darauf ist, dass das Beispiel IList<T>/ICollection<T> falsch verwendet. Wenn Sie eine ICollection<T> verwenden, müssen Sie das IsReadOnly-Flag überprüfen.

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

Wenn Ihnen jemand ein Array oder eine Liste übergibt, wird Ihr Code gut funktionieren, wenn Sie das Flag jedes Mal überprüfen und einen Fallback haben... Aber mal ehrlich: Wer macht das schon? Wissen Sie nicht im Voraus, ob Ihre Methode eine Liste benötigt, die zusätzliche Mitglieder aufnehmen kann; geben Sie das nicht in der Signatur der Methode an? Was genau würden Sie tun, wenn Ihnen eine schreibgeschützte Liste übergeben würde wie int[] ?

Sie können eine List<T> in Code, der die IList<T> / ICollection<T> richtig . Sie kann nicht garantieren, dass Sie eine IList<T> / ICollection<T> in Code, der die List<T> .

Viele Argumente für die Verwendung von Abstraktionen anstelle von konkreten Typen berufen sich auf das Prinzip der einzigen Verantwortung bzw. das Prinzip der Schnittstellentrennung, d. h. sie hängen von der engstmöglichen Schnittstelle ab. In den meisten Fällen, wenn Sie eine List<T> und Sie meinen, Sie könnten stattdessen eine engere Schnittstelle verwenden - warum nicht? IEnumerable<T> ? Dies ist oft die bessere Lösung, wenn Sie keine Gegenstände hinzufügen müssen. Wenn Sie der Sammlung etwas hinzufügen müssen, verwenden Sie den konkreten Typ, List<T> .

Für mich IList<T> (und ICollection<T> ) ist der schlimmste Teil des .NET-Frameworks. IsReadOnly verstößt gegen den Grundsatz der geringsten Überraschung. Eine Klasse, wie zum Beispiel Array die niemals das Hinzufügen, Einfügen oder Entfernen von Elementen erlaubt, sollte keine Schnittstelle mit Add-, Insert- und Remove-Methoden implementieren. (siehe auch https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties )

Ist IList<T> eine gute Ergänzung für Ihre Organisation? Wenn ein Kollege Sie bittet, eine Methodensignatur zu ändern, um die IList<T> anstelle von List<T> fragen Sie sie, wie sie ein Element zu einer Tabelle hinzufügen würden. IList<T> . Wenn sie nicht wissen, dass IsReadOnly (und die meisten Leute tun das nicht), dann verwenden Sie nicht IList<T> . Niemals.


Beachten Sie, dass das IsReadOnly-Flag von ICollection<T> stammt und anzeigt, ob Elemente hinzugefügt oder aus der Sammlung entfernt werden können; aber um die Dinge wirklich zu verwirren, zeigt es nicht an, ob sie ersetzt werden können, was im Fall von Arrays (die IsReadOnlys == true zurückgeben) sein kann.

Weitere Informationen zu IsReadOnly finden Sie unter msdn Definition von ICollection<T>.IsReadOnly

10 Stimmen

Großartige, großartige Antwort. Ich möchte noch hinzufügen, dass, selbst wenn IsReadOnly es true für eine IList können Sie die aktuellen Mitglieder noch ändern! Vielleicht sollte diese Eigenschaft folgendermaßen benannt werden IsFixedSize ?

0 Stimmen

(das mit dem Bestehen eines Tests getestet wurde Array als IList deshalb konnte ich die derzeitigen Mitglieder ändern)

1 Stimmen

@SamPearson gab es eine IsFixedSize-Eigenschaft auf IList in .NET 1, nicht in der generischen IList<T> von .NET 2.0 enthalten, wo sie mit IsReadOnly konsolidiert wurde, was zu einem überraschenden Verhalten bei Arrays führte. Hier gibt es einen ausführlichen Blog über die subtilen Unterschiede, der das Gehirn schmelzen lässt: enterprisecraftsmanship.com/2014/11/22/

39voto

ILoveFortran Punkte 3281

List<T> ist eine spezielle Implementierung von IList<T> der ein Container ist, der auf die gleiche Weise wie ein lineares Array adressiert werden kann T[] unter Verwendung eines ganzzahligen Indexes. Wenn Sie angeben IList<T> als Typ des Argumentes der Methode geben Sie nur an, dass Sie bestimmte Fähigkeiten des Containers benötigen.

So schreibt die Schnittstellenspezifikation beispielsweise keine bestimmte Datenstruktur vor, die verwendet werden muss. Die Implementierung von List<T> erreicht die gleiche Leistung beim Zugriff, Löschen und Hinzufügen von Elementen wie ein lineares Array. Man könnte sich jedoch eine Implementierung vorstellen, die stattdessen auf einer verknüpften Liste basiert, bei der das Hinzufügen von Elementen am Ende billiger ist (konstante Zeit), der zufällige Zugriff jedoch viel teurer. (Beachten Sie, dass die .NET LinkedList<T> fait no implementieren IList<T> .)

Dieses Beispiel zeigt Ihnen auch, dass es Situationen geben kann, in denen Sie die Implementierung und nicht die Schnittstelle in der Argumentliste angeben müssen: In diesem Beispiel immer dann, wenn Sie ein bestimmtes Merkmal der Zugriffsleistung benötigen. Dies ist in der Regel für eine bestimmte Implementierung eines Containers gewährleistet ( List<T> Dokumentation: "Es implementiert die IList<T> generische Schnittstelle mit Hilfe eines Arrays, dessen Größe bei Bedarf dynamisch erhöht wird").

Außerdem sollten Sie in Erwägung ziehen, nur die wenigsten Funktionen freizugeben, die Sie benötigen. Wenn Sie z.B. den Inhalt der Liste nicht ändern müssen, sollten Sie wahrscheinlich die Verwendung von IEnumerable<T> die IList<T> erweitert.

0 Stimmen

Bezüglich Ihrer letzten Aussage über die Verwendung von IEnumerable anstelle von IList. Dies ist nicht immer empfohlen, nehmen Sie WPF zum Beispiel, die nur ein Wrapper-IList-Objekt erstellen wird, so wird eine Auswirkung der Perf - haben. msdn.microsoft.com/de-us/library/bb613546.aspx

1 Stimmen

Gute Antwort: Leistungsgarantien sind etwas, das eine Schnittstelle nicht bieten kann. In manchen Codes kann dies sehr wichtig sein, und die Verwendung konkreter Klassen vermittelt Ihre Absicht, Ihren Bedarf an dieser speziellen Klasse. Eine Schnittstelle hingegen besagt: "Ich muss nur diesen Satz von Methoden aufrufen, kein anderer Vertrag ist impliziert."

1 Stimmen

@joshperry Wenn Sie sich an der Leistung einer Schnittstelle stören, sind Sie in erster Linie auf der falschen Plattform. Impo, das ist Mikro-Optimierung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X