2009-09-21 10 views
10

Ok, ich hoffe, dass die Gemeinschaft als Ganzes uns bei der Lösung einer seit einiger Zeit andauernden Debatten am Arbeitsplatz helfen wird. Dies hat mit dem Definieren von Schnittstellen zu tun, die Listen eines Typs annehmen oder zurückgeben. Es gibt mehrere Möglichkeiten, dies zu tun:Welche bevorzugen Sie für Schnittstellen: T [], IEnumerable <T>, IList <T> oder andere?

public interface Foo 
{ 
    Bar[] Bars { get; } 
    IEnumerable<Bar> Bars { get; } 
    ICollection<Bar> Bars { get; } 
    IList<Bar> Bars { get; } 
} 

Meine eigene Präferenz für Argumente und Arrays für Rückgabewerte IEnumerable zu verwenden ist:

public interface Foo 
{ 
    void Do(IEnumerable<Bar> bars); 
    Bar[] Bars { get; } 
} 

für diesen Ansatz Mein Argument ist, dass die Implementierungsklasse ein erstellen Listen Sie direkt von IEnumerable auf und geben Sie es einfach mit List.ToArray() zurück. Einige glauben jedoch, dass IList anstelle eines Arrays zurückgegeben werden sollte. Das Problem, das ich hier habe, ist, dass du es jetzt erneut brauchst, um es mit einer ReadOnlyCollection zu kopieren, bevor du zurückkehrst. Die Option, IEnumerable zurückzugeben, scheint für den Clientcode problematisch zu sein.

Was verwenden/bevorzugen Sie? (besonders in Bezug auf Bibliotheken, die von anderen Entwicklern außerhalb Ihrer Organisation verwendet werden)

+1

Ich habe die Fehler meiner Wege gesehen. Wie viele Zustand unten ist es offensichtlich Entwickler sehen T [] Eigenschaften nicht als Ich glaube, mein Missbrauch von ihnen hat mir einige schlechte Angewohnheiten beigebracht. Ich habe mich nie wirklich darum gekümmert, ob sie das Array modifiziert haben, weil es eine Kopie war. Aber ich habe die Verwirrung, die es hervorruft, erkannt Gewinner –

Antwort

18

Meine Präferenz ist IEnumerable<T>. Jede andere der vorgeschlagenen Schnittstellen gibt dem Benutzer die Möglichkeit, die zugrunde liegende Sammlung zu ändern. Dies ist mit ziemlicher Sicherheit nicht das, was Sie tun möchten, da es den Kunden erlaubt, stillschweigend eine interne Sammlung zu ändern.

Ein anderer guter IMHO, ist ReadOnlyCollection<T>. Es ermöglicht alle Spaß .Count und Indexer Eigenschaften und sagt eindeutig zum Verbraucher "Sie können meine Daten nicht ändern".

+3

+1 in jeder Anwendung, an der ich gearbeitet habe, zumindest, IEnumerable ist alles, was Sie brauchen 98% der Zeit –

+0

Nicht zu sagen, du liegst falsch, aber nicht davon aus, dass du nicht bist neu abstimmen a readonly Liste oder eine flache Kopie (oder sonstwie unveränderlich machen)? – annakata

+1

@annakata, aber wie drückt man dies Ihren Verbrauchern aus? Sie können nicht einfach auf eine Rückkehr von say IList schauen und wissen: 1) sollte ich das nicht modifizieren, 2) ist die Modifikation durch Werfen deaktiviert (Wörterbuch :: Werte) oder 3) wird eine vollständige Kopie zurückgegeben. Es ist einfach nicht offensichtlich von der Verwendung. Returning IEnumerable auf der anderen Seite sagt ziemlich eindeutig "Sie meine Daten nicht berühren". – JaredPar

15

ich nicht Arrays zurückgeben - sie wirklich eine schreckliche Rückgabetyp zu verwenden sind, wenn eine API zu schaffen - wenn Sie wirklich ein veränderliches Sequenz, die die IList<T> oder ICollection<T> Schnittstelle benötigen oder eine konkrete Rück Collection<T> statt.

Auch würde ich vorschlagen, dass Sie Arrays considered somewhat harmful von Eric Lippert lesen:

ich von einem Autor der Programmiersprache eine moralische Frage bekam Lehrbücher der neulich meine Meinungen zu anfordernden ob oder nicht Programmierer Anfänger sollte gelehrt werden, wie Arrays zu verwenden sind.

Anstatt tatsächlich antworten, dass Frage, ich ihm eine lange Liste meiner Meinungen über Arrays gab, wie ich Arrays verwenden, wie wir Arrays erwarten in der Zukunft verwendet werden, und so weiter. Diese wird ein bisschen lang, aber wie Pascal, ich hatte keine Zeit, um es kürzer zu machen.

mich mit den Worten beginnen lassen, wenn Sie definitiv nicht Arrays verwenden sollten, und dann Wachs philosophischeren über die Zukunft der modernen Programmierung und die Rolle des Arrays in der kommenden Welt.

+1

Hatte nicht gelesen, dass die Lippert Verbindung vor - +1 für die allein – annakata

+0

Dito - gut zu lesen. –

+2

Danke für den Ruf. :) –

8

Für Immobilien Sammlungen, (und die Indizes haben notwendige semantische Bedeutung) indiziert sind, sollten Sie ReadOnlyCollection<T> verwenden (nur lesen) oder IList<T> (Lesen/Schreiben). Es ist das flexibelste und ausdrucksvollste. Verwenden Sie für nicht indizierte Sammlungen IEnumerable<T> (schreibgeschützt) oder (Lesen/Schreiben).

Methodenparameter sollten IEnumerable<T> verwenden, wenn sie 1) müssen zum Hinzufügen/Entfernen von Elementen der Sammlung (ICollection<T>) oder 2) benötigen Indizes für necesary semantische Zwecke (IList<T>). Wenn die Methode von der Indizierung der Verfügbarkeit profitieren kann (z. B. eine Sortierroutine), kann sie immer as IList<T> oder .ToList() verwenden, wenn dies bei der Implementierung fehlschlägt.

+0

+1 für die Verwendung, was für das Szenario geeignet ist ... es ist wirklich nicht 1 zu Regel dann alle ... eine andere Möglichkeit zu sagen, ReadOnlyCollection vs. IEnumerable ist vorinstalliert vs. Lazy geladen. – eglasius

1

Ich würde IEnumerable bevorzugen, da es die höchste Ebene der Schnittstellen ist, die dem Endbenutzer die Möglichkeit gibt, neu zu gestalten, wie er es wünscht. Auch wenn dies dem Benutzer zu Beginn nur minimale Funktionalität bietet (im Grunde nur Enumeration), wäre es dennoch ausreichend, um praktisch jeden Bedarf abzudecken, insbesondere mit den Erweiterungsmethoden ToArray(), ToList() usw.

1

Ich denke darüber in Bezug auf das Schreiben der nützlichsten Code möglich: Code, der mehr tun kann.

In diesen Begriffen bedeutet es, dass ich gerne die schwächste Schnittstelle als Methode Argumente akzeptieren, weil das macht meinen Code von mehr Orten nützlich. In diesem Fall ist das ein IEnumerable<T>. Hast du ein Array? Sie können meine Methode aufrufen. Haben Sie eine Liste? Sie können meine Methode aufrufen. Habe einen Iteratorblock? Du hast die Idee.

Es bedeutet auch, dass ich mag meine Methoden, um die stärkste Schnittstelle zurückgeben, die bequem ist, so dass Code, der auf der Methode beruht, leicht mehr tun kann. In diesem Fall wäre das IList<T>. Beachten Sie, dass dies nicht bedeutet, dass ich eine Liste erstellen werde, damit ich sie zurückgeben kann. Es bedeutet nur, dass wenn ich bereits einige habe, die IList<T> implementiert, kann ich es auch verwenden.

Beachten Sie, dass ich etwas unkonventionell in Bezug auf Rückgabetypen bin. Ein typischerer Ansatz besteht darin, auch schwächere Typen aus Methoden zurückzugeben, um zu vermeiden, dass Sie sich in eine bestimmte Implementierung einklinken.

+1

... wenn ich bereits einige habe, die IList implementiert, kann ich es auch verwenden ... Ja, ich verstehe Ihren Standpunkt; Dies kann jedoch seltsame Nebenwirkungen verursachen. Man sollte es minimal in eine ReadOnlyCollection () wickeln. –

+1

@csharptest.net, wenn Sie argumentieren, dass ein IList in ReadOnly Collection gepackt werden sollte, warum würden Sie dann ein Array yourself zurückgeben, die weit von readonly ist, nachdem alle Elemente durch beliebige zuweisbare Typen –

+0

ersetzt werden können Sie sind hier richtig; Mein irreführender Glaube war, dass es offensichtlich wäre, dass Sie eine Kopie bekommen würden und dass es keinen Sinn hätte, sie zu modifizieren. Ich gebe zu, dass ich bei dieser Diskussion eindeutig falsch lag ... Es kam mir nie in den Sinn, dass Leute "Foo.Bars [0] = x" oder irgendeinen solchen Unsinn tun würden. Wie auch immer, danke für die Hilfe, um mich zu erleuchten +1 :) –

0

IEnumerable < T> ist sehr nützlich für lazy-evaluated-Iteration, insbesondere in Szenarien, die Methodenverkettung verwenden.

Aber als Rückgabetyp für ein typisches Datenzugriffs Tier ist eine Count-Eigenschaft oft nützlich, und ich würde es vorziehen, ein ICollection < T> mit einer Count-Eigenschaft oder möglicherweise IList < T>, wenn ich typische Verbraucher denken zurückkehren werden möchte einen Indexer verwenden.

Dies ist auch ein Hinweis für den Aufrufer, dass die Sammlung tatsächlich materialisiert wurde. Und so kann der Aufrufer die zurückgegebene Auflistung durchlaufen, ohne dass Ausnahmen von der Datenzugriffsebene auftreten. Dies kann wichtig sein. Beispielsweise kann ein Dienst einen Datenstrom (z. B. SOAP) aus der zurückgegebenen Sammlung generieren. Es kann unangenehm sein, wenn beim Generieren des Streams aufgrund der lazy-evaluated-Iteration eine Ausnahme von der Datenzugriffsebene ausgelöst wird, da der Ausgabestream bereits teilweise geschrieben wird, wenn die Ausnahme ausgelöst wird.

0

Da die Linq-Erweiterungsmethoden zu IEnumerable <T> hinzugefügt wurden, habe ich festgestellt, dass meine Verwendung der anderen Schnittstellen erheblich zurückgegangen ist; wahrscheinlich um 80%. Ich benutzte die Liste <T> religiös, da sie Methoden hatte, die Delegaten für faule Auswertungen wie Find, FindAll, ForEach und dergleichen akzeptierten. Da dies über System.Linq-Erweiterungen verfügbar ist, habe ich alle diese Referenzen durch IEnumerable <T> Referenzen ersetzt.

0

Ich würde nicht mit Array gehen, es ist ein Typ, der Modifizierung erlaubt, hat aber keine hinzufügen/entfernen ... Art von wie das Schlimmste der Packung.Wenn ich Änderungen zulassen möchte, würde ich einen Typ verwenden, der add/remove unterstützt.

Wenn Sie Änderungen verhindern wollen, wickeln Sie es bereits/kopieren es, so sehe ich nicht, was mit einem IEnumerable oder einer ReadOnlyCollection falsch ist. Ich würde mit dem späteren gehen ... etwas, das ich an IEnumerable nicht mag, ist, dass es von Natur aus faul ist, aber wenn Sie mit vorgeladenen Daten nur zum Umwickeln verwenden, tendiert das Aufrufen von Code, der damit arbeitet, dazu, geladene Daten oder haben zusätzliche "unnötige" Zeilen :(... das kann hässliche Ergebnisse während der Änderung bekommen.

Verwandte Themen