Ich bin verwirrt darüber, welchen Auflistungstyp ich von meinen öffentlichen API-Methoden und -Eigenschaften zurückgeben soll.
Die Sammlungen , die ich im Sinn haben sind IList
, ICollection
und Collection
.
Wird die Rückgabe eines dieser Typen immer den anderen vorgezogen oder hängt dies von der jeweiligen Situation ab?
c#
.net
generics
collections
Rocky Singh
quelle
quelle
Antworten:
Im Allgemeinen sollten Sie einen Typ zurückgeben, der so allgemein wie möglich ist, dh einen Typ, der gerade genug über die zurückgegebenen Daten weiß, die der Verbraucher verwenden muss. Auf diese Weise haben Sie mehr Freiheit, die Implementierung der API zu ändern, ohne den Code zu beschädigen, der sie verwendet.
Betrachten Sie auch die
IEnumerable<T>
Schnittstelle als Rückgabetyp. Wenn das Ergebnis nur iteriert werden soll, benötigt der Verbraucher nicht mehr.quelle
Count
überprüft die Implementierung der Methode den tatsächlichen Typ der Auflistung, sodass die EigenschaftenLength
oderCount
für einige bekannte Typen wie Arrays verwendet werden undICollection
selbst wenn Sie sie als angebenIEnumerable
.Thing<T>
implementiert,IList<T>
aber nicht das generischeICollection
, das AufrufenIEnumerable<Cat>.Count()
von aThing<Cat>
schnell ist, das AufrufenIEnumerable<Animal>.Count()
jedoch langsam ist (da die Erweiterungsmethode nach einer Implementierung von suchen und diese nicht finden würdeICollection<Cat>
). Wenn die Klasse das Nicht-Generische implementiertICollection
,IEnumerable<Animal>.Count
würde sie dies jedoch finden und verwenden.ICollection<T>
ist eine Schnittstelle , die Sammlung Semantik wie aussetztAdd()
,Remove()
undCount
.Collection<T>
ist eine konkrete Implementierung derICollection<T>
Schnittstelle.IList<T>
ist im Wesentlichen einICollection<T>
mit zufälliger Reihenfolge basierender Zugriff.In diesem Fall sollten Sie entscheiden, ob Ihre Ergebnisse eine Listensemantik wie die auftragsbasierte Indizierung erfordern (dann verwenden
IList<T>
) oder ob Sie nur eine ungeordnete "Tasche" von Ergebnissen zurückgeben müssen (dann verwendenICollection<T>
).quelle
Collection<T>
GeräteIList<T>
und nicht nurICollection<T>
.IEnumerable<T>
bestellt. Was unterscheidetIList<T>
ausICollection<T>
ist , dass es Mitglieder an der Arbeit mit Indizes bietet. Zum Beispiellist[5]
funktioniert, wird abercollection[5]
nicht kompiliert.IList<T>
. Es gibt viele bestellte Sammlungen, die dies nicht tun.IList<T>
geht es um schnellen indizierten Zugriff.IEnumerable<T>
bestellt? TakeHashSet<T>
- es implementiertIEnumerable<T>
, ist aber eindeutig nicht bestellt. Das heißt, Sie haben keinen Einfluss auf die Reihenfolge der Elemente und diese Reihenfolge kann sich jederzeit ändern, wenn die interne Hash-Tabelle neu organisiert wird.Der Hauptunterschied zwischen
IList<T>
undICollection<T>
besteht darin,IList<T>
dass Sie über einen Index auf Elemente zugreifen können.IList<T>
beschreibt Array-ähnliche Typen. Auf Elemente in einemICollection<T>
kann nur durch Aufzählung zugegriffen werden. Beide ermöglichen das Einfügen und Löschen von Elementen.Wenn Sie nur eine Sammlung aufzählen müssen,
IEnumerable<T>
ist dies vorzuziehen. Es hat zwei Vorteile gegenüber den anderen:Änderungen an der Sammlung sind nicht zulässig (jedoch nicht an den Elementen, wenn sie vom Referenztyp sind).
Es ermöglicht die größtmögliche Vielfalt an Quellen, einschließlich Aufzählungen, die algorithmisch generiert werden und überhaupt keine Sammlungen sind.
Collection<T>
ist eine Basisklasse, die hauptsächlich für Implementierer von Sammlungen nützlich ist. Wenn Sie es in Schnittstellen (APIs) verfügbar machen, werden viele nützliche Sammlungen ausgeschlossen, die nicht davon abgeleitet sind.Ein Nachteil von
IList<T>
ist, dass Arrays es implementieren, Sie jedoch keine Elemente hinzufügen oder entfernen können (dh Sie können die Array-Länge nicht ändern). Eine Ausnahme wird ausgelöst, wenn SieIList<T>.Add(item)
ein Array aufrufen . Die Situation ist etwas entschärft, ebensoIList<T>
wie eine boolesche EigenschaftIsReadOnly
, die Sie überprüfen können, bevor Sie dies versuchen. Aber in meinen Augen ist dies immer noch ein Designfehler in der Bibliothek . Daher verwende ichList<T>
direkt, wenn die Möglichkeit zum Hinzufügen oder Entfernen von Elementen erforderlich ist.quelle
Collection
,ReadOnlyCollection
oderKeyedCollection
. Und dasList
sollte nicht zurückgegeben werden.IList<T>
, kann nicht sichergestellt werden, dass die Methode nicht mit einem Array als Parameter aufgerufen wird.IList<T>
ist die Basisschnittstelle für alle generischen Listen. Da es sich um eine geordnete Sammlung handelt, kann die Implementierung über die Reihenfolge entscheiden, die von der sortierten Reihenfolge bis zur Einfügereihenfolge reicht. Darüber hinausIlist
verfügt Item über die Eigenschaft Item, mit der Methoden Einträge in der Liste basierend auf ihrem Index lesen und bearbeiten können. Dies ermöglicht das Einfügen und Entfernen eines Werts in / aus der Liste an einem Positionsindex.Auch da stehen hier auch
IList<T> : ICollection<T>
alle Methoden vonICollection<T>
zur Implementierung zur Verfügung.ICollection<T>
ist die Basisschnittstelle für alle generischen Sammlungen. Es definiert Größe, Enumeratoren und Synchronisationsmethoden. Sie können ein Element zu einer Sammlung hinzufügen oder daraus entfernen, aber Sie können nicht auswählen, an welcher Position es aufgrund des Fehlens einer Indexeigenschaft geschieht.Collection<T>
eine Implementierung siehtIList<T>
,IList
undIReadOnlyList<T>
.Wenn Sie einen engeren Schnittstellentyp verwenden, z. B.
ICollection<T>
anstelle vonIList<T>
, schützen Sie Ihren Code vor Änderungen. Wenn Sie einen breiteren Schnittstellentyp verwenden, z. B.IList<T>
besteht die Gefahr, dass Codeänderungen beschädigt werden.Zitat aus einer Quelle ,
quelle
Die Rückgabe eines Schnittstellentyps ist allgemeiner, daher würde ich mich (ohne weitere Informationen zu Ihrem speziellen Anwendungsfall) dazu neigen. Wenn Sie die Indizierungsunterstützung verfügbar machen möchten, wählen Sie
IList<T>
, andernfallsICollection<T>
reicht dies aus. Wenn Sie angeben möchten, dass die zurückgegebenen Typen schreibgeschützt sind, wählen SieIEnumerable<T>
.Und, falls Sie es nicht vorher gelesen haben, Brad Abrams und Krzysztof Cwalina ein großes Buch schrieb dem Titel „Framework Design Guidelines: Conventions, Idiome und Muster für wiederverwendbare .NET - Bibliotheken“ (Sie können ein Download von verdauen hier ).
quelle
IEnumerable<T>
ist schreibgeschützt? Sicher, aber wenn Sie es in einem Repository-Kontext neu abstimmen, ist es auch nach der Ausführung von ToList nicht threadsicher. Problem ist, wenn die ursprüngliche Datenquelle GC erhält, dann funktioniert IEnumarble.ToList () nicht in einem Multithread-Kontext. Es funktioniert linear, da GC aufgerufen wird, nachdem Sie die Arbeitasync
erledigt haben, aber jetzt einen Tag in 4.5+ IEnumarbale zu verwenden, wird zu einem Ballschmerz.Es gibt einige Themen, die aus dieser Frage stammen:
Möglicherweise möchten Sie hervorheben, dass es sich um eine objektorientierte API handelt
Schnittstellen versus Klassen
Wenn Sie nicht viel Erfahrung mit Schnittstellen haben, empfehle ich, sich an Klassen zu halten. Ich sehe viele Male, wie Entwickler zu Schnittstellen springen, auch wenn dies nicht unbedingt erforderlich ist.
Und am Ende ein schlechtes Interface-Design anstelle eines guten Klassendesigns, das übrigens irgendwann auf ein gutes Interface-Design migriert werden kann ...
Sie werden viele Schnittstellen in der API sehen, aber beeilen Sie sich nicht, wenn Sie sie nicht benötigen.
Sie werden schließlich lernen, wie Sie Schnittstellen auf Ihren Code anwenden.
Welche spezifische Klasse aus mehreren ähnlichen Klassen, Sammlungen, Listen, Arrays?
In c # (Dotnet) gibt es mehrere Klassen, die ausgetauscht werden können. Wenn Sie, wie bereits erwähnt, etwas aus einer spezifischeren Klasse wie "CanBeSortedClass" benötigen, machen Sie es in Ihrer API explizit.
Muss Ihr API-Benutzer wirklich wissen, dass Ihre Klasse sortiert werden kann, oder ein bestimmtes Format auf die Elemente anwenden? Verwenden Sie dann "CanBeSortedClass" oder "ElementsCanBePaintedClass", andernfalls "GenericBrandClass".
Verwenden Sie andernfalls eine allgemeinere Klasse.
Gemeinsame Sammlungsklassen im Vergleich zu Sammlungen von Unterelementen ("Generika")
Sie werden feststellen, dass es Klassen gibt, die andere Elemente enthalten, und Sie können angeben, dass alle Elemente von einem bestimmten Typ sein sollen.
Generische Sammlungen sind Klassen, mit denen Sie dieselbe Sammlung für mehrere Codeanwendungen verwenden können, ohne für jeden neuen Unterelementtyp eine neue Sammlung erstellen zu müssen, z. B.: Sammlung .
Benötigt Ihr API-Benutzer einen ganz bestimmten Typ, der für alle Elemente gleich ist?
Verwenden Sie so etwas wie
List<WashingtonApple>
.Benötigt Ihr API-Benutzer mehrere verwandte Typen?
Expose
List<Fruit>
für Ihre API und verwendenList<Orange>
List<Banana>
,List<Strawberry>
intern, woOrange
,Banana
undStrawberry
sind Nachkommen ausFruit
.Benötigt Ihr API-Benutzer eine generische Typensammlung?
Verwenden Sie
List
, wo alle Elemente sindobject
.Prost.
quelle