Frage

Vor C#-Generika codierte jeder Sammlungen für seine Geschäftsobjekte, indem er eine Sammlungsbasis erstellte, die IEnumerable implementierte

IE:

public class CollectionBase : IEnumerable

und würden daraus dann ihre Business Object-Sammlungen ableiten.

public class BusinessObjectCollection : CollectionBase

Benutzt irgendjemand stattdessen einfach die generische Listenklasse?Ich habe festgestellt, dass ich einen Kompromiss der beiden Techniken verwende:

public class BusinessObjectCollection : List<BusinessObject>

Ich mache das, weil ich gerne stark typisierte Namen habe, anstatt nur Listen herumzureichen.

Was ist dein Ansatz?

War es hilfreich?

Lösung

Ich bin generell der Meinung, eine Liste einfach direkt zu verwenden, es sei denn, ich muss aus irgendeinem Grund die Datenstruktur kapseln und eine begrenzte Teilmenge ihrer Funktionalität bereitstellen.Das liegt vor allem daran, dass es reine Zeitverschwendung ist, wenn ich keinen besonderen Bedarf an Kapselung habe.

Allerdings gibt es mit der Aggregatinitialisierungsfunktion in C# 3.0 einige neue Situationen, in denen ich die Verwendung benutzerdefinierter Sammlungsklassen empfehlen würde.

Grundsätzlich erlaubt C# 3.0 jede Klasse, die implementiert IEnumerable und verfügt über eine Add-Methode zur Verwendung der neuen Aggregatinitialisierer-Syntax.Da Dictionary beispielsweise eine Methode Add(K-Taste, V-Wert) definiert, ist es möglich, ein Wörterbuch mit dieser Syntax zu initialisieren:

var d = new Dictionary<string, int>
{
    {"hello", 0},
    {"the answer to life the universe and everything is:", 42}
};

Das Tolle an der Funktion ist, dass sie für Add-Methoden mit einer beliebigen Anzahl von Argumenten funktioniert.Nehmen wir zum Beispiel diese Sammlung:

class c1 : IEnumerable
{
    void Add(int x1, int x2, int x3)
    {
        //...
    }

    //...
}

Es wäre möglich, es folgendermaßen zu initialisieren:

var x = new c1
{
    {1,2,3},
    {4,5,6}
}

Dies kann sehr nützlich sein, wenn Sie statische Tabellen mit komplexen Objekten erstellen müssen.Zum Beispiel, wenn Sie gerade verwendet haben List<Customer> und Sie wollten eine statische Liste von Kundenobjekten erstellen, die Sie wie folgt erstellen müssten:

var x = new List<Customer>
{
    new Customer("Scott Wisniewski", "555-555-5555", "Seattle", "WA"),
    new Customer("John Doe", "555-555-1234", "Los Angeles", "CA"),
    new Customer("Michael Scott", "555-555-8769", "Scranton PA"),
    new Customer("Ali G", "", "Staines", "UK")
}

Wenn Sie jedoch eine benutzerdefinierte Sammlung wie diese verwenden:

class CustomerList  : List<Customer>
{
    public void Add(string name, string phoneNumber, string city, string stateOrCountry)
    {
        Add(new Customer(name, phoneNumber, city, stateOrCounter));
    }
}

Anschließend können Sie die Sammlung mit dieser Syntax initialisieren:

var customers = new CustomerList
{
    {"Scott Wisniewski", "555-555-5555", "Seattle", "WA"},
    {"John Doe", "555-555-1234", "Los Angeles", "CA"},
    {"Michael Scott", "555-555-8769", "Scranton PA"},
    {"Ali G", "", "Staines", "UK"}
}

Dies hat den Vorteil, dass es sowohl einfacher einzugeben als auch leichter zu lesen ist, da nicht für jedes Element der Elementtypname erneut eingegeben werden muss.Der Vorteil kann besonders groß sein, wenn der Elementtyp lang oder komplex ist.

Allerdings ist dies nur dann sinnvoll, wenn Sie in Ihrer App definierte statische Datensammlungen benötigen.Einige Arten von Apps, wie z. B. Compiler, verwenden sie ständig.Bei anderen, etwa typischen Datenbank-Apps, ist dies nicht der Fall, da sie alle ihre Daten aus einer Datenbank laden.

Mein Rat wäre, dass Sie eine benutzerdefinierte Sammlungsklasse erstellen, wenn Sie entweder eine statische Sammlung von Objekten definieren oder die Sammlungsschnittstelle wegkapseln müssen.Ansonsten würde ich einfach verwenden List<T> direkt.

Andere Tipps

Es ist empfohlen dass in öffentlichen APIs nicht List<T>, sondern Collection<T> verwendet werden soll

Wenn Sie jedoch davon erben, sollte es Ihnen gut gehen, afaik.

Ich nutze es lieber einfach List<BusinessObject>.Durch die Typdefinition wird dem Code lediglich unnötiges Boilerplate hinzugefügt. List<BusinessObject> ist ein bestimmter Typ, es ist nicht irgendein Typ List Objekt, daher ist es immer noch stark typisiert.

Noch wichtiger ist, etwas zu erklären List<BusinessObject> macht es für jeden, der den Code liest, einfacher, zu erkennen, mit welchen Typen er es zu tun hat, und muss nicht erst durchsuchen, um herauszufinden, was für ein BusinessObjectCollection ist und dann denken Sie daran, dass es nur eine Liste ist.Durch die Typdefinition müssen Sie eine konsistente (Um-)Benennungskonvention fordern, die jeder befolgen muss, damit sie einen Sinn ergibt.

Verwenden Sie den Typ List<BusinessObject> wo Sie eine Liste davon deklarieren müssen.Wo Sie jedoch eine Liste von zurückgeben BusinessObject, erwägen Sie eine Rückkehr IEnumerable<T>, IList<T> oder ReadOnlyCollection<T> - d.h.Geben Sie den schwächsten Vertrag zurück, der den Kunden zufriedenstellt.

Wenn Sie einer Liste „benutzerdefinierten Code hinzufügen“ möchten, verwenden Sie Codeerweiterungsmethoden für den Listentyp.Fügen Sie diese Methoden auch hier dem schwächsten möglichen Vertrag hinzu, z.

public static int SomeCount(this IEnumerable<BusinessObject> someList)

Natürlich können und sollten Sie mit Erweiterungsmethoden keinen Status hinzufügen. Wenn Sie also eine neue Eigenschaft und ein Feld dahinter hinzufügen müssen, verwenden Sie eine Unterklasse oder besser eine Wrapper-Klasse, um dies zu speichern.

Ich habe zwei Optionen hin und her geprüft:

public class BusinessObjectCollection : List<BusinessObject> {}

oder Methoden, die einfach Folgendes tun:

public IEnumerable<BusinessObject> GetBusinessObjects();

Der Vorteil des ersten Ansatzes besteht darin, dass Sie den zugrunde liegenden Datenspeicher ändern können, ohne sich mit Methodensignaturen herumschlagen zu müssen.Wenn Sie von einem Sammlungstyp erben, der eine Methode aus der vorherigen Implementierung entfernt, müssen Sie sich leider im gesamten Code mit diesen Situationen auseinandersetzen.

Sie sollten es wahrscheinlich vermeiden, zu diesem Zweck eine eigene Sammlung zu erstellen.Es kommt häufig vor, dass man bei Refactorings oder beim Hinzufügen neuer Funktionen mehrmals die Art der Datenstruktur ändern möchte.Mit Ihrem Ansatz würden Sie am Ende eine separate Klasse für BusinessObjectList, BusinessObjectDictionary, BusinessObjectTree usw. erhalten.

Ich sehe keinen wirklichen Wert darin, diese Klasse zu erstellen, nur weil der Klassenname besser lesbar ist.Ja, die Syntax der spitzen Klammern ist etwas hässlich, aber sie ist Standard in C++, C# und Java. Selbst wenn Sie also keinen Code schreiben, der sie verwendet, werden Sie ständig darauf stoßen.

Ich leite meine eigenen Sammlungsklassen im Allgemeinen nur ab, wenn ich einen „Mehrwert“ benötige.Zum Beispiel, wenn die Sammlung selbst einige „Metadaten“-Eigenschaften mit Tags versehen müsste.

Ich mache genau das Gleiche wie du, Jonathan...einfach erben von List<T>.Sie erhalten das Beste aus beiden Welten.Aber ich mache das im Allgemeinen nur, wenn es einen Mehrwert gibt, wie zum Beispiel das Hinzufügen eines LoadAll() Methode oder was auch immer.

Sie können beides verwenden.Aus Gründen der Faulheit – ich meine Produktivität – ist List eine sehr nützliche Klasse, sie ist außerdem „umfassend“ und ehrlich gesagt voller YANGNI-Mitglieder.Gepaart mit der vernünftigen Argumentation/Empfehlung der MSDN-Artikel Ich habe bereits über die Offenlegung von List als öffentliches Mitglied verlinkt, ich bevorzuge den „dritten“ Weg:

Persönlich verwende ich das Dekoratormuster, um nur das anzuzeigen, was ich aus der Liste benötige, d. h.:

public OrderItemCollection : IEnumerable<OrderItem> 
{
    private readonly List<OrderItem> _orderItems = new List<OrderItem>();

    void Add(OrderItem item)
    {
         _orderItems.Add(item)
    }

    //implement only the list members, which are required from your domain. 
    //ie. sum items, calculate weight etc...

    private IEnumerator<string> Enumerator() {
        return _orderItems.GetEnumerator();
    }

    public IEnumerator<string> GetEnumerator() {
        return Enumerator();
    }    
}

Darüber hinaus würde ich OrderItemCollection wahrscheinlich in IOrderItemCollection abstrahieren, damit ich meine Implementierung von IOrderItemCollection in Zukunft austauschen kann (möglicherweise bevorzuge ich die Verwendung eines anderen inneren aufzählbaren Objekts wie Collection oder wahrscheinlicher für die Verwendung einer Schlüssel-Wert-Paar-Sammlung oder eines Schlüssel-Wert-Paar-Sets). .

Ich verwende generische Listen für fast alle Szenarien.Ich würde die Verwendung einer abgeleiteten Sammlung nur noch in Betracht ziehen, wenn ich sammlungsspezifische Mitglieder hinzufüge.Mit dem Aufkommen von LINQ ist jedoch sogar die Notwendigkeit dafür gesunken.

6 von 1, ein halbes Dutzend von einem anderen

So oder so ist es das Gleiche.Ich mache das nur, wenn ich einen Grund habe, benutzerdefinierten Code zur BusinessObjectCollection hinzuzufügen.

Ohne dass die Lademethoden eine Liste zurückgeben, kann ich mehr Code in einer gemeinsamen generischen Klasse schreiben und es einfach funktionieren lassen.Zum Beispiel eine Load-Methode.

Wie jemand anderes betonte, wird empfohlen, List nicht öffentlich zugänglich zu machen, und FxCop wird jammern, wenn Sie dies tun.Dazu gehört das Erben von List wie folgt:

public MyTypeCollection : List<MyType>

In den meisten Fällen stellen öffentliche APIs IList (oder ICollection oder IEnumerable) entsprechend zur Verfügung.

In Fällen, in denen Sie Ihre eigene benutzerdefinierte Sammlung wünschen, können Sie FxCop ruhig halten, indem Sie von Collection statt von List erben.

Wenn Sie Ihre eigene Sammlungsklasse erstellen möchten, sollten Sie sich die Typen in ansehen System.Collections.ObjectModel-Namespace.

Der Namespace definiert Basisklassen, die es Implementierern erleichtern sollen, benutzerdefinierte Sammlungen zu erstellen.

Ich mache es eher mit meiner eigenen Sammlung, wenn ich den Zugriff auf die eigentliche Liste schützen möchte.Wenn Sie Geschäftsobjekte schreiben, benötigen Sie wahrscheinlich einen Hook, um zu wissen, ob Ihr Objekt hinzugefügt/entfernt wird. In diesem Sinne halte ich BOCollection für die bessere Idee.Wenn dies nicht erforderlich ist, ist List natürlich einfacher.Möglicherweise möchten Sie auch die Verwendung von IList prüfen, um eine zusätzliche Abstraktionsschnittstelle bereitzustellen, wenn Sie eine Art Proxy benötigen (z. B.eine gefälschte Sammlung löst Lazy Load aus der Datenbank aus)

Aber...Warum nicht Castle ActiveRecord oder ein anderes ausgereiftes ORM-Framework in Betracht ziehen?:) :)

Meistens wähle ich einfach die Listenmethode, da sie mir in 90 % der Fälle die gesamte Funktionalität bietet, die ich benötige, und wenn etwas „Extra“ benötigt wird, erbe ich davon und codiere dieses zusätzliche Bit .

Ich würde das tun:

using BusinessObjectCollection = List<BusinessObject>;

Dadurch wird lediglich ein Alias ​​erstellt und kein völlig neuer Typ.Ich ziehe es vor, List<BusinessObject> direkt zu verwenden, weil es mir die Freiheit gibt, die zugrunde liegende Struktur der Sammlung zu einem späteren Zeitpunkt zu ändern, ohne den Code zu ändern, der sie verwendet (solange ich dieselben Eigenschaften und Methoden bereitstelle).

Probieren Sie Folgendes aus:

System.Collections.ObjectModel.Collection<BusinessObject>

Es ist nicht erforderlich, eine grundlegende Methode wie CollectionBase zu implementieren

das ist der Weg:

Arrays zurückgeben, akzeptieren IEnumerable<T>

=)

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