it-swarm.com.de

Sammlung <T> versus Liste <T> Was sollten Sie auf Ihren Schnittstellen verwenden?

Der Code sieht wie folgt aus:

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

Wenn ich eine Code-Analyse durchführe, erhalte ich die folgende Empfehlung.

Warnung 3 CAe1002: Microsoft.Design: Ändern Sie "List" in "IMyClass.GetList ()", um "Collection", "ReadOnlyCollection" oder "KeyedCollection" zu verwenden

Wie soll ich das beheben und was ist hier eine gute Praxis?

150
bovium

Um den "Warum" -Teil der Frage zu beantworten, warum nicht List<T>, Die Gründe sind Zukunftssicherheit und API-Einfachheit.

zukunftssicher

List<T> ist nicht so konzipiert, dass es durch Unterklassen leicht erweiterbar ist. Es ist so konzipiert, dass es für interne Implementierungen schnell ist. Sie werden bemerken, dass die Methoden nicht virtuell sind und daher nicht überschrieben werden können, und es gibt keine Verknüpfungen mit den Add/Insert/Remove -Operationen.

Dies bedeutet, dass Sie den Typ ändern müssen, wenn Sie das Verhalten der Sammlung in Zukunft ändern müssen (z. B. um Nullobjekte abzulehnen, die hinzugefügt werden sollen, oder um in diesem Fall zusätzliche Arbeit zu leisten, z. B. um Ihren Klassenstatus zu aktualisieren) Von der Sammlung, zu der Sie zurückkehren, können Sie eine Unterklasse bilden, was eine Unterbrechung der Schnittstellenänderung darstellt (natürlich kann eine Änderung der Semantik von Dingen wie dem Nichtzulassen von Null auch eine Schnittstellenänderung sein, aber Dinge wie das Aktualisieren Ihres internen Klassenstatus wären dies nicht).

Also, indem Sie entweder eine Klasse zurückgeben, die leicht untergeordnet werden kann, wie Collection<T> oder eine Schnittstelle wie IList<T>, ICollection<T> oder IEnumerable<T> Sie können Ihre interne Implementierung in einen anderen Auflistungstyp ändern, um Ihre Anforderungen zu erfüllen, ohne den Code der Verbraucher zu verletzen, da er weiterhin als der Typ zurückgegeben werden kann, den sie erwarten.

API-Einfachheit

List<T> enthält viele nützliche Operationen wie BinarySearch, Sort und so weiter. Wenn es sich jedoch um eine Sammlung handelt, die Sie verfügbar machen, haben Sie wahrscheinlich die Kontrolle über die Semantik der Liste und nicht über die Konsumenten. Während Ihre Klasse diese Operationen möglicherweise intern benötigt, ist es sehr unwahrscheinlich, dass die Verbraucher Ihrer Klasse sie aufrufen möchten (oder sollten).

Wenn Sie eine einfachere Auflistungsklasse oder -schnittstelle anbieten, verringern Sie die Anzahl der Mitglieder, die den Benutzern Ihrer API angezeigt werden, und erleichtern deren Verwendung.

217
Greg Beech

Ich würde persönlich erklären, dass es eher eine Schnittstelle als eine konkrete Sammlung zurückgibt. Wenn Sie wirklich auf die Liste zugreifen möchten, verwenden Sie IList<T> . Ansonsten betrachte ICollection<T> und IEnumerable<T> .

48
Jon Skeet

Ich glaube, noch hat niemand auf das "Warum" geantwortet. Der Grund, warum Sie einen Collection<T> Anstelle eines List<T> Verwenden sollten, liegt darin, dass jeder, der Zugriff auf Ihr Objekt erhält, den List<T> Ändern kann Elemente in der Liste. Während Collection<T> Anzeigen soll, dass Sie Ihre eigenen Methoden "Hinzufügen", "Entfernen" usw. erstellen.

Sie müssen sich wahrscheinlich keine Sorgen machen, da Sie die Schnittstelle wahrscheinlich nur für sich selbst (oder für einige Kollegen) programmieren. Hier ist ein weiteres Beispiel, das möglicherweise Sinn ergibt.

Wenn Sie ein öffentliches Array haben, z.

public int[] MyIntegers { get; }

Sie würden das denken, weil es nur einen "get" -Zugriff gibt, mit dem niemand mit den Werten herumspielen kann, aber das stimmt nicht. Jeder kann die Werte dort wie folgt ändern:

someObject.MyIngegers[3] = 12345;

Ich persönlich würde in den meisten Fällen nur List<T> Verwenden. Wenn Sie jedoch eine Klassenbibliothek entwerfen, die Sie zufälligen Entwicklern zur Verfügung stellen, und sich auf den Status der Objekte verlassen müssen, sollten Sie Ihre eigene Sammlung erstellen und von dort aus sperren: )

2
Timothy Khouri

Es geht hauptsächlich darum, Ihre eigenen Implementierungen zu abstrahieren, anstatt das zu manipulierende List-Objekt direkt verfügbar zu machen.

Es ist nicht empfehlenswert, andere Objekte (oder Personen) den Status Ihrer Objekte direkt ändern zu lassen. Denken Sie an Immobiliensucher/-setter.

Sammlung -> Für normale Sammlung
ReadOnlyCollection -> Für Sammlungen, die nicht geändert werden sollten
KeyedCollection -> Wenn Sie stattdessen Wörterbücher möchten.

Wie dies behoben wird, hängt davon ab, was Ihre Klasse tun soll und welchen Zweck die Methode GetList () hat. Können Sie näher darauf eingehen?

1
chakrit

In solchen Fällen versuche ich normalerweise, den geringsten Implementierungsaufwand aufzudecken. Wenn die Verbraucher nicht wissen müssen, dass Sie tatsächlich eine Liste verwenden, müssen Sie keine Liste zurückgeben. Wenn Sie zurückkehren, während Microsoft eine Sammlung vorschlägt, verbergen Sie die Tatsache, dass Sie eine Liste für die Konsumenten Ihrer Klasse verwenden, und isolieren sie gegen eine interne Änderung.

1

Etwas hinzuzufügen, obwohl es lange her ist, dass dies gefragt wurde.

Wenn Ihr Listentyp von List<T> Anstelle von Collection<T> Abgeleitet ist, können Sie die geschützten virtuellen Methoden, die Collection<T> Implementiert, nicht implementieren. Dies bedeutet, dass der abgeleitete Typ nicht reagieren kann, wenn Änderungen an der Liste vorgenommen werden. Dies liegt daran, dass List<T> Voraussetzt, dass Sie wissen, wann Sie Elemente hinzufügen oder entfernen. Die Möglichkeit, auf Benachrichtigungen zu antworten, ist ein Overhead und daher bietet List<T> Dies nicht an.

In Fällen, in denen externer Code Zugriff auf Ihre Sammlung hat, haben Sie möglicherweise keine Kontrolle darüber, wann ein Element hinzugefügt oder entfernt wird. Daher bietet Collection<T> Eine Möglichkeit, um zu erfahren, wann Ihre Liste geändert wurde.

1
NullReference

Ich sehe kein Problem damit, so etwas zurückzugeben

this.InternalData.Filter(crteria).ToList();

Wenn ich ein nicht verbundenes Kopie der internen Daten oder ein nicht verbundenes Ergebnis einer Datenabfrage zurückgegeben habe, kann ich sicher List<TItem> Zurückgeben, ohne Implementierungsdetails preiszugeben, und die Verwendung der zurückgegebenen Daten in zulassen der bequeme Weg.

Aber das hängt davon ab, welchen Verbrauchertyp ich erwarte - wenn es sich um ein Datenraster handelt, ziehe ich es vor, IEnumerable<TItem>das wird die kopierte Liste der Artikel sein in den meisten Fällen ohnehin :)

0