Frage

Jedes Mal, wenn ich ein Objekt mit einer Sammlungseigenschaft erstelle, überlege ich, wie ich das am besten machen kann.

  1. öffentliches Eigentum mit einem Getter, der einen Verweis auf private Variable zurückgibt
  2. Explizite Methoden get_objlist und set_objlist, die jedes Mal neue oder geklonte Objekte zurückgeben und erstellen
  3. Explizite get_objlist, die einen IEnumerator und eine set_objlist zurückgibt, die IEnumerator nimmt

Macht es einen Unterschied, ob die Sammlung ein Array (d. h. objList.Clone()) oder eine Liste ist?

Wenn die Rückgabe der eigentlichen Sammlung als Referenz so schlecht ist, weil dadurch Abhängigkeiten entstehen, warum dann eine Eigenschaft als Referenz zurückgeben?Jedes Mal, wenn Sie ein untergeordnetes Objekt als Referenz verfügbar machen, können die Interna dieses untergeordneten Objekts geändert werden, ohne dass das übergeordnete Objekt „wissen“, es sei denn, das untergeordnete Objekt verfügt über ein Eigenschaftsänderungsereignis.Besteht die Gefahr von Speicherlecks?

Und unterbrechen die Optionen 2 und 3 nicht die Serialisierung?Ist das ein Haken 22 oder müssen Sie jedes Mal eine benutzerdefinierte Serialisierung implementieren, wenn Sie eine Sammlungseigenschaft haben?

Die generische ReadOnlyCollection scheint ein guter Kompromiss für den allgemeinen Gebrauch zu sein.Es umschließt eine IList und schränkt den Zugriff darauf ein.Möglicherweise hilft dies bei Speicherlecks und Serialisierung.Allerdings ist es immer noch so Bedenken hinsichtlich der Aufzählung

Vielleicht kommt es einfach darauf an.Wenn es Ihnen egal ist, dass die Sammlung geändert wird, stellen Sie sie einfach als öffentlichen Accessor über eine private Variable gemäß Nr. 1 bereit.Wenn Sie nicht möchten, dass andere Programme die Sammlung ändern, ist Nr. 2 und/oder Nr. 3 besser.

Die Frage impliziert, warum eine Methode einer anderen vorgezogen werden sollte und welche Auswirkungen dies auf Sicherheit, Speicher, Serialisierung usw. hat.

War es hilfreich?

Lösung

Wie Sie eine Sammlung verfügbar machen, hängt vollständig davon ab, wie Benutzer damit interagieren sollen.

1) Wenn Benutzer Elemente zur Sammlung eines Objekts hinzufügen und daraus entfernen, ist eine einfache Nur-Get-Sammlungseigenschaft am besten (Option Nr. 1 aus der ursprünglichen Frage):

private readonly Collection<T> myCollection_ = new ...;
public Collection<T> MyCollection {
  get { return this.myCollection_; }
}

Diese Strategie wird für die verwendet Items Sammlungen auf WindowsForms und WPF ItemsControl Steuerelemente, bei denen Benutzer Elemente hinzufügen und entfernen, die das Steuerelement anzeigen soll.Diese Steuerelemente veröffentlichen die eigentliche Sammlung und verwenden Rückrufe oder Ereignis-Listener, um den Überblick über Elemente zu behalten.

WPF stellt außerdem einige einstellbare Sammlungen bereit, damit Benutzer eine Sammlung von Elementen anzeigen können, die sie steuern, z. B. die ItemsSource Eigentum auf ItemsControl (Option Nr. 3 aus der ursprünglichen Frage).Dies ist jedoch kein häufiger Anwendungsfall.


2) Wenn Benutzer nur vom Objekt verwaltete Daten lesen, können Sie eine schreibgeschützte Sammlung verwenden, z Streitig empfohlen:

private readonly List<T> myPrivateCollection_ = new ...;
private ReadOnlyCollection<T> myPrivateCollectionView_;
public ReadOnlyCollection<T> MyCollection {
  get {
    if( this.myPrivateCollectionView_ == null ) { /* lazily initialize view */ }
    return this.myPrivateCollectionView_;
  }
}

Beachten Sie, dass ReadOnlyCollection<T> Bietet eine Live-Ansicht der zugrunde liegenden Sammlung, sodass Sie die Ansicht nur einmal erstellen müssen.

Wenn die interne Sammlung nicht implementiert wird IList<T>, oder wenn Sie den Zugriff auf fortgeschrittenere Benutzer beschränken möchten, können Sie stattdessen den Zugriff auf die Sammlung über einen Enumerator umschließen:

public IEnumerable<T> MyCollection {
  get {
    foreach( T item in this.myPrivateCollection_ )
      yield return item;
  }
}

Dieser Ansatz ist einfach zu implementieren und bietet außerdem Zugriff auf alle Mitglieder, ohne die interne Sammlung offenzulegen.Allerdings muss die Sammlung unverändert bleiben, da die BCL-Sammlungsklassen eine Ausnahme auslösen, wenn Sie versuchen, eine Sammlung aufzulisten, nachdem sie geändert wurde.Wenn sich die zugrunde liegende Sammlung wahrscheinlich ändern wird, können Sie entweder einen leichten Wrapper erstellen, der die Sammlung sicher auflistet, oder eine Kopie der Sammlung zurückgeben.


3) Wenn Sie schließlich Arrays anstelle von Sammlungen höherer Ebenen verfügbar machen müssen, sollten Sie eine Kopie des Arrays zurückgeben, um zu verhindern, dass Benutzer es ändern (Option Nr. 2 aus der ursprünglichen Frage):

private T[] myArray_;
public T[] GetMyArray( ) {
  T[] copy = new T[this.myArray_.Length];
  this.myArray_.CopyTo( copy, 0 );
  return copy;
  // Note: if you are using LINQ, calling the 'ToArray( )' 
  //  extension method will create a copy for you.
}

Sie sollten das zugrunde liegende Array nicht über eine Eigenschaft offenlegen, da Sie nicht erkennen können, wann Benutzer es ändern.Um das Ändern des Arrays zu ermöglichen, können Sie entweder ein entsprechendes hinzufügen SetMyArray( T[] array ) -Methode oder verwenden Sie einen benutzerdefinierten Indexer:

public T this[int index] {
  get { return this.myArray_[index]; }
  set {
    // TODO: validate new value; raise change event; etc.
    this.myArray_[index] = value;
  }
}

(Durch die Implementierung eines benutzerdefinierten Indexers duplizieren Sie natürlich die Arbeit der BCL-Klassen :)

Andere Tipps

Normalerweise verwende ich diesen öffentlichen Getter, der System.Collections.ObjectModel.ReadOnlyCollection zurückgibt:

public ReadOnlyCollection<SomeClass> Collection
{
    get
    {
         return new ReadOnlyCollection<SomeClass>(myList);
    }
}

Und öffentliche Methoden für das Objekt, um die Sammlung zu ändern.

Clear();
Add(SomeClass class);

Wenn die Klasse ein Repository sein soll, mit dem sich andere Leute herumschlagen können, lege ich einfach die private Variable gemäß Methode Nr. 1 offen, da dadurch das Schreiben einer eigenen API eingespart wird, aber im Produktionscode scheue ich mich eher davor.

Wenn Sie lediglich eine Sammlung in Ihrer Instanz verfügbar machen möchten, erscheint mir die Verwendung eines Getter/Setters für eine private Mitgliedsvariable als die sinnvollste Lösung (Ihre erste vorgeschlagene Option).

Warum ist die Verwendung von ReadOnlyCollection(T) Ihrer Meinung nach ein Kompromiss?Wenn Sie weiterhin Änderungsbenachrichtigungen für die ursprünglich verpackte IList erhalten müssen, können Sie auch a verwenden ReadOnlyObservableCollection(T) um Ihre Sammlung zu verpacken.Wäre dies in Ihrem Szenario ein geringerer Kompromiss?

Ich bin ein Java-Entwickler, aber ich denke, das gilt auch für C#.

Ich mache niemals eine private Sammlungseigenschaft verfügbar, da andere Teile des Programms sie ändern können, ohne dass die Eltern es bemerken, sodass ich in der Getter-Methode ein Array mit den Objekten der Sammlung zurückgebe und in der Setter-Methode a aufrufe clearAll() über die Sammlung und dann ein addAll()

ReadOnlyCollection hat immer noch den Nachteil, dass der Verbraucher nicht sicher sein kann, dass die ursprüngliche Sammlung nicht zu einem ungünstigen Zeitpunkt geändert wird.Stattdessen können Sie verwenden Unveränderliche Sammlungen.Wenn Sie eine Änderung vornehmen müssen, erhalten Sie statt des Originals eine geänderte Kopie.Durch die Art und Weise, wie es implementiert wird, ist es mit der Leistung der veränderlichen Sammlungen konkurrenzfähig.Oder noch besser, wenn Sie das Original nicht mehrmals kopieren müssen, um anschließend an jeder Kopie eine Reihe unterschiedlicher (inkompatibler) Änderungen vorzunehmen.

Ich empfehle, das Neue zu verwenden IReadOnlyList<T> Und IReadOnlyCollection<T> Schnittstellen zum Offenlegen einer Sammlung (erfordert .NET 4.5).

Beispiel:

public class AddressBook
{
    private readonly List<Contact> contacts;

    public AddressBook()
    {
        this.contacts = new List<Contact>();
    }

    public IReadOnlyList<Contact> Contacts { get { return contacts; } }

    public void AddContact(Contact contact)
    {
        contacts.Add(contact);
    }

    public void RemoveContact(Contact contact)
    {
        contacts.Remove(contact);
    }
}

Wenn Sie sicherstellen müssen, dass die Sammlung nicht von außen manipuliert werden kann, sollten Sie dies in Betracht ziehen ReadOnlyCollection<T> oder die neuen Immutable-Kollektionen.

Vermeiden Nutzung der Schnittstelle IEnumerable<T> eine Sammlung freilegen.Diese Schnittstelle definiert keine Garantie dafür, dass mehrere Aufzählungen ordnungsgemäß funktionieren.Wenn IEnumerable eine Abfrage darstellt, wird die Abfrage bei jeder Aufzählung erneut ausgeführt.Entwickler, die eine Instanz von IEnumerable erhalten, wissen nicht, ob es sich um eine Sammlung oder eine Abfrage handelt.

Mehr zu diesem Thema können Sie hier lesen Wiki-Seite.

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