Frage

Kann mir jemand erklären, warum ich Ilist über List in C#verwenden möchte?

Verwandte Frage: Warum wird es als schlecht angesehen zu entlarven List<T>

War es hilfreich?

Lösung

Wenn Sie Ihre Klasse über eine Bibliothek freisetzen, die andere verwenden, möchten Sie sie im Allgemeinen über Schnittstellen und nicht über konkrete Implementierungen freilegen. Dies hilft, wenn Sie sich entscheiden, die Implementierung Ihrer Klasse später zu ändern, um eine andere konkrete Klasse zu verwenden. In diesem Fall müssen die Benutzer Ihrer Bibliothek ihren Code nicht aktualisieren, da sich die Benutzeroberfläche nicht ändert.

Wenn Sie es nur intern verwenden, kümmern Sie sich möglicherweise nicht so sehr und verwenden Sie es List<T> Vielleicht ok.

Andere Tipps

Die weniger beliebte Antwort ist Programmierer, so zu tun, als würde ihre Software weltweit wiederverwendet werden, wenn die meisten Projekte von einer kleinen Anzahl von Menschen aufrechterhalten werden und wie auch gute Schnittstellen-Soundbites sind, detaillieren Sie täuschen dich selbst.

Architektur -Astronauten. Die Chancen, die Sie jemals Ihren eigenen Ilist schreiben, der zu den bereits im .NET -Framework etwas hinzufügt, sind so abgelegen, dass es sich um theoretische Gelee -Tots handelt, die für "Best Practices" reserviert sind.

Software astronauts

Wenn Sie gefragt werden, welche Sie in einem Interview verwenden, sagen Sie offensichtlich Ilist, lächeln und beide sehen sich darüber freuen, dass Sie so klug sind. Oder für eine öffentliche API, ilist. Hoffentlich bekommst du meinen Standpunkt.

Schnittstelle ist ein Versprechen (oder ein Vertrag).

Wie es immer mit den Versprechen ist - kleiner umso besser.

Einige Leute sagen "immer benutze IList<T> Anstatt von List<T>".
Sie möchten, dass Sie Ihre Methodensignaturen ändern void Foo(List<T> input) zu void Foo(IList<T> input).

Diese Leute sind falsch.

Es ist nuancierter als das. Wenn Sie eine zurückgeben IList<T> Im Rahmen der öffentlichen Schnittstelle zu Ihrer Bibliothek lassen Sie sich interessante Optionen, um in Zukunft möglicherweise eine benutzerdefinierte Liste zu erstellen. Möglicherweise brauchen Sie diese Option nie, 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 schweren Fehler.

Als kleines Gegenargument finden Sie möglicherweise, dass jeder einzelne Anrufer a braucht List<T> Wie auch immer, und der Anrufcode ist übersät mit .ToList()

Aber vor allem, vor allem, Wenn Sie einen Ilist als Parameter akzeptieren, sollten Sie besser vorsichtig sein, weil IList<T> und List<T> Verhalten Sie sich nicht genauso. Trotz der Ähnlichkeit des Namens und trotz des Teilens eines Schnittstelle tun sie nicht das gleiche entdecken Vertrag.

Angenommen, Sie haben diese Methode:

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

Ein hilfreicher Kollege "Refactors" die Methode zum Akzeptieren IList<int>.

Ihr Code ist jetzt kaputt, weil int[] Geräte IList<int>, ist aber von fester Größe. Der Vertrag für ICollection<T> (die Basis von IList<T>) Erfordert den Code, der ihn verwendet, um das zu überprüfen IsReadOnly Fahnen Sie vor dem Versuch, Elemente aus der Sammlung hinzuzufügen oder zu entfernen. Der Vertrag für List<T> nicht.

Das Liskov -Substitutionsprinzip (vereinfacht) besagt, dass ein abgeleiteter Typ anstelle eines Basistyps verwendet werden sollte, ohne zusätzliche Voraussetzungen oder Postkonditionen.

Dies fühlt sich so an, als würde es das Liskov -Substitutionsprinzip zerbrechen.

 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 es tut es nicht. Die Antwort darauf ist, dass das Beispiel ilist verwendet wurdeu003CT> /ICollectionu003CT> falsch. Wenn Sie eine iCollection verwendenu003CT> Sie müssen die Isreadonly -Flagge ü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 jemand an Ihnen ein Array oder eine Liste besteht, funktioniert Ihr Code gut, wenn Sie jedes Mal das Flag überprüfen und einen Fallback haben ... aber wirklich; Wer macht das? Sie wissen nicht im Voraus, ob Ihre Methode eine Liste benötigt, die zusätzliche Mitglieder aufnehmen kann. Geben Sie das nicht in der Methodensignatur an? Was genau wollten Sie tun, wenn Sie eine LEAD -Liste wie nur bestanden haben? int[]?

Sie können a ersetzen List<T> in Code, der verwendet IList<T>/ICollection<T> korrekt. Du kann nicht garantieren, dass Sie eine ersetzen können IList<T>/ICollection<T> in Code, der verwendet List<T>.

Das Prinzipationsprinzip der einzelnen Verantwortung / Schnittstellensegregation ist in vielen Argumenten zur Verwendung von Abstraktionen anstelle von konkreten Typen ein Reiz - abhängig von der engsten Schnittstelle. In den meisten Fällen, wenn Sie a verwenden List<T> Und Sie denken, Sie könnten stattdessen eine schmalere Oberfläche verwenden - warum nicht IEnumerable<T>? Dies passt oft besser, wenn Sie keine Artikel hinzufügen müssen. Wenn Sie der Sammlung hinzufügen müssen, verwenden Sie den Betontyp. List<T>.

Für mich IList<T> (und ICollection<T>) ist der schlimmste Teil des .NET -Frameworks. IsReadOnly verstößt gegen das Prinzip der geringsten Überraschung. Eine Klasse, wie z. Array, Das Hinzufügen, Einfügen oder Entfernen von Elementen kann eine Schnittstelle nicht mit Hinzufügen, Einfügen und Entfernen von Methoden implementieren. (siehe auch https://softwareEngineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-properties)

Ist IList<T> Eine gute Passform für Ihre Organisation? Wenn ein Kollege Sie auffordert, eine Methodensignatur zu ändern, um sie zu verwenden IList<T> Anstatt von List<T>, Fragen Sie sie, wie sie einem ein Element hinzufügen würden IList<T>. Wenn sie nichts wissen IsReadOnly (Und die meisten Leute nicht), dann nicht benutzen IList<T>. Je.


Beachten Sie, dass die Flagge isReadonly von der Icollection stammtu003CT> und gibt an, ob Elemente aus der Sammlung hinzugefügt oder entfernt werden können; Aber nur um die Dinge wirklich zu verwirren, zeigt es nicht an, ob sie ersetzt werden können, was bei Arrays (die IsReadonlys == True zurückgeben können) sein kann.

Weitere Informationen zu isReadonly finden Sie unter MSDN -Definition der iCollectionu003CT> .Isreadonly

List<T> ist eine spezifische Implementierung von IList<T>, das ist ein Container, der genauso angegangen werden kann wie ein lineares Array T[] Verwenden eines Ganzzahlindex. Wenn Sie angeben IList<T> Als Argument der Methode geben Sie nur an, dass Sie bestimmte Funktionen des Containers benötigen.

Beispielsweise erzwingt die Schnittstellenspezifikation eine bestimmte zu verwendende Datenstruktur nicht. Die Implementierung von List<T> geschieht mit der gleichen Leistung für den Zugriff, das Löschen und Hinzufügen von Elementen als lineares Array. Sie können sich jedoch eine Implementierung vorstellen, die stattdessen durch eine verknüpfte Liste unterstützt wird, für die das Hinzufügen von Elementen zum Ende billiger ist (konstante Zeit), aber zufällig viel teurer. (Beachten Sie, dass das .net LinkedList<T> tut nicht implementieren IList<T>.)

In diesem Beispiel wird auch angegeben, dass es möglicherweise Situationen gibt, in denen Sie die Implementierung und nicht die Schnittstelle in der Argumentliste angeben müssen: In diesem Beispiel, wenn Sie eine bestimmte Zugriffsleistung Merkmale benötigen. Dies wird normalerweise für eine bestimmte Implementierung eines Containers garantiert (List<T> Dokumentation: "es implementiert die IList<T> Generische Schnittstelle unter Verwendung eines Arrays, dessen Größe dynamisch erhöht ist, wie dies erforderlich ist. ").

Darüber hinaus möchten Sie möglicherweise in Betracht ziehen, die geringsten Funktionen aufzudecken, die Sie benötigen. Zum Beispiel. Wenn Sie den Inhalt der Liste nicht ändern müssen, sollten Sie wahrscheinlich in Betracht ziehen IEnumerable<T>, die IList<T> erweitert.

Ich würde die Frage ein wenig umgeben, anstatt zu rechtfertigen, warum Sie die Schnittstelle über die konkrete Implementierung verwenden sollten, versuchen Sie zu begründen, warum Sie die konkrete Implementierung und nicht die Schnittstelle verwenden würden. Wenn Sie es nicht rechtfertigen können, verwenden Sie die Schnittstelle.

Ilistu003CT> ist eine Schnittstelle, sodass Sie eine andere Klasse erben und ILIST dennoch implementieren könnenu003CT> während der Erbenlisteu003CT> verhindert, dass Sie dies tun.

Zum Beispiel, wenn es eine Klasse A und Ihre Klasse B erbt, können Sie die Liste nicht verwendenu003CT>

class A : B, IList<T> { ... }

Ein Prinzip von TDD und OOP ist im Allgemeinen die Programmierung an einer Schnittstelle, keine Implementierung.

In diesem speziellen Fall, da Sie im Wesentlichen über ein Sprachkonstrukt sprechen, nicht über ein benutzerdefiniertes, ist es im Allgemeinen keine Rolle, aber sagen Sie beispielsweise, dass Sie die Liste gefunden haben, die Sie nicht benötigten. Wenn Sie Ilist im Rest der App verwendet hätten, könnten Sie die Liste mit Ihrer eigenen Klasse erweitern und diese immer noch ohne Wiederaufbauspiegel übergeben können.

Die Kosten dafür sind minimal. Warum sparen Sie sich nicht später die Kopfschmerzen? Darum geht es beim Schnittstellenprinzip.

public void Foo(IList<Bar> list)
{
     // Do Something with the list here.
}

In diesem Fall könnten Sie in jeder Klasse bestehen, die den Ilist implementiertu003CBar> Schnittstelle. Wenn Sie die Liste verwendet habenu003CBar> Stattdessen nur eine Listeu003CBar> Instanz könnte eingegangen werden.

Der Ilistu003CBar> Weg ist locker gekoppelt als die Listeu003CBar> Weg.

Der wichtigste Fall für die Verwendung von Schnittstellen über Implementierungen liegt in den Parametern zu Ihrer API. Wenn Ihre API einen Listenparameter enthält, muss jeder, der sie verwendet, die Liste verwenden. Wenn der Parametertyp ilist ist, hat der Anrufer viel mehr Freiheit und kann Klassen verwenden, von dem Sie noch nie gehört haben, was möglicherweise nicht einmal existiert hat, als Ihr Code geschrieben wurde.

Supprising that none of these List vs IList questions (or answers) mentions the signature difference. (Which is why I searched for this question on SO!)

So here's the methods contained by List that are not found in IList, at least as of .NET 4.5 (circa 2015)

  • AddRange
  • AsReadOnly
  • BinarySearch
  • Capacity
  • ConvertAll
  • Exists
  • Find
  • FindAll
  • FindIndex
  • FindLast
  • FindLastIndex
  • ForEach
  • GetRange
  • InsertRange
  • LastIndexOf
  • RemoveAll
  • RemoveRange
  • Reverse
  • Sort
  • ToArray
  • TrimExcess
  • TrueForAll

What if .NET 5.0 replaces System.Collections.Generic.List<T> to System.Collection.Generics.LinearList<T>. .NET always owns the name List<T> but they guarantee that IList<T> is a contract. So IMHO we (atleast I) are not supposed to use someone's name (though it is .NET in this case) and get into trouble later.

In case of using IList<T>, the caller is always guareented things to work, and the implementer is free to change the underlying collection to any alternative concrete implementation of IList

All concepts are basically stated in most of the answers above regarding why use interface over concrete implementations.

IList<T> defines those methods (not including extension methods)

IList<T> MSDN link

  1. Add
  2. Clear
  3. Contains
  4. CopyTo
  5. GetEnumerator
  6. IndexOf
  7. Insert
  8. Remove
  9. RemoveAt

List<T> implements those nine methods (not including extension methods), on top of that it has about 41 public methods, which weighs in your consideration of which one to use in your application.

List<T> MSDN link

You would because defining an IList or an ICollection would open up for other implementations of your interfaces.

You might want to have an IOrderRepository that defines a collection of orders in either a IList or ICollection. You could then have different kinds of implementations to provide a list of orders as long as they conform to "rules" defined by your IList or ICollection.

The interface ensures that you at least get the methods you are expecting; being aware of the definition of the interface ie. all abstract methods that are there to be implemented by any class inheriting the interface. so if some one makes a huge class of his own with several methods besides the ones he inherited from the interface for some addition functionality, and those are of no use to you, its better to use a reference to a subclass (in this case the interface) and assign the concrete class object to it.

additional advantage is that your code is safe from any changes to concrete class as you are subscribing to only few of the methods of concrete class and those are the ones that are going to be there as long as the concrete class inherits from the interface you are using. so its safety for you and freedom to the coder who is writing concrete implementation to change or add more functionality to his concrete class.

IList<> is almost always preferable as per the other poster's advice, however note there is a bug in .NET 3.5 sp 1 when running an IList<> through more than one cycle of serialization / deserialization with the WCF DataContractSerializer.

There is now a SP to fix this bug : KB 971030

You can look at this argument from several angles including the one of a purely OO approach which says to program against an Interface not an implementation. With this thought, using IList follows the same principal as passing around and using Interfaces that you define from scratch. I also believe in the scalability and flexibility factors provided by an Interface in general. If a class implmenting IList<T> needs to be extended or changed, the consuming code does not have to change; it knows what the IList Interface contract adheres to. However using a concrete implementation and List<T> on a class that changes, could cause the calling code to need to be changed as well. This is because a class adhering to IList<T> guarantees a certain behavior that is not guaranteed by a concrete type using List<T>.

Also having the power to do something like modify the default implementation of List<T> on a class Implementing IList<T> for say the .Add, .Remove or any other IList method gives the developer a lot of flexibility and power, otherwise predefined by List<T>

Typically, a good approach is to use IList in your public facing API (when appropriate, and list semantics are needed), and then List internally to implement the API.  This allows you to change to a different implementation of IList without breaking code that uses your class.

The class name List may be changed in next .net framework but the interface is never going to change as interface is contract.

Note that, if your API is only going to be used in foreach loops, etc, then you might want to consider just exposing IEnumerable instead.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top