Warum ist ReadOnlyObservableCollection.CollectionChanged nicht öffentlich?

84

Warum ist ReadOnlyObservableCollection.CollectionChangedgeschützt und nicht öffentlich (wie das entsprechende ObservableCollection.CollectionChangedist)?

Was nützt eine implementierte Sammlung, INotifyCollectionChangedwenn ich nicht auf das CollectionChangedEreignis zugreifen kann ?

Oskar
quelle
1
Aus Neugier, warum sollten Sie eine schreibgeschützte Sammlung erwarten sein zu ändern? Dann würde es doch nicht nur gelesen werden?
workmad3
80
Gegenfrage: Warum sollte ich nicht erwarten, dass sich eine ObservableCollection ändert? Was nützt es, es zu beobachten, wenn sich nichts ändern wird? Nun, die Sammlung wird sich definitiv ändern, aber ich habe Verbraucher, die sie nur beobachten dürfen. Schauen, aber nicht berühren ...
Oskar
1
Ich bin kürzlich auch auf dieses Problem gestoßen. Grundsätzlich implementiert ObservableCollection das geänderte Ereignis INotifyCollection nicht ordnungsgemäß. Warum erlaubt C # einer Klasse, Zugriffsschnittstellenereignisse einzuschränken, jedoch keine Schnittstellenmethoden?
DJSKinner
32
Ich muss dafür stimmen, dass dies totaler Wahnsinn ist. Warum in aller Welt gibt es ReadOnlyObservableCollection überhaupt, wenn Sie CollectionChanged-Ereignisse nicht abonnieren können? WTF ist der Punkt? Und für alle, die immer wieder sagen, dass sich eine schreibgeschützte Sammlung niemals ändern wird, sollten Sie wirklich gründlich darüber nachdenken, was Sie sagen.
MojoFilter
8
"schreibgeschützt" bedeutet nicht "unveränderlich", wie manche zu denken scheinen. Dies bedeutet nur, dass Code, der die Sammlung nur über eine solche Eigenschaft sehen kann, diese nicht ändern darf. Sie kann tatsächlich geändert werden, wenn Code Mitglieder über die zugrunde liegende Sammlung hinzufügt oder entfernt. Die Leser sollten weiterhin benachrichtigt werden können, dass Änderungen vorgenommen wurden, auch wenn sie die Sammlung nicht selbst ändern können. Möglicherweise müssen sie noch selbst auf Änderungen reagieren. Ich kann mir keinen gültigen Grund vorstellen, die CollectionChanged-Eigenschaft einzuschränken, wie dies in diesem Fall geschehen ist.
Gil

Antworten:

16

Ich habe einen Weg für Sie gefunden, wie das geht:

ObservableCollection<string> obsCollection = new ObservableCollection<string>();
INotifyCollectionChanged collection = new ReadOnlyObservableCollection<string>(obsCollection);
collection.CollectionChanged += new NotifyCollectionChangedEventHandler(collection_CollectionChanged);

Sie müssen nur explizit über die INotifyCollectionChanged- Schnittstelle auf Ihre Sammlung verweisen .

Restuta
quelle
14
Bitte nicht, dass ReadOnlyObservableCollection ein Wrapper um eine ObservableCollection ist - was sich ändern wird. Verbraucher der ReadOnlyObservableCollection dürfen diese Änderungen nur beobachten, selbst nichts ändern.
Oskar
Sie sollten sich nicht auf diese Tatsache verlassen, die schreibgeschützte Sammlung wird sich in keinem Fall ändern, da sie als "schreibgeschützt" bezeichnet wird. Das ist mein Verständnis.
Restuta
4
+1 für die Gießlösung. Es ist jedoch nicht logisch, eine schreibgeschützte Sammlung zu beobachten: Wenn Sie nur Lesezugriff auf eine Datenbank hätten, würden Sie erwarten, dass sich diese niemals ändert?
DJSKinner
16
Ich sehe nicht, was die Schwierigkeit hier ist. Die ReadOnlyObservableCollection ist eine Klasse, die eine READ-ONLY-Möglichkeit zum Beobachten einer Sammlung bietet. Wenn meine Klasse beispielsweise eine Sammlung von Sitzungen enthält und ich nicht möchte, dass Personen Sitzungen zu meiner Sammlung hinzufügen oder daraus entfernen können, diese aber dennoch beobachten, ist ReadOnlyObservableCollection das perfekte Mittel dafür. Die Sammlung ist nur für die Benutzer meiner Klasse lesbar, aber ich habe eine Lese- / Schreibkopie für meinen eigenen Gebrauch.
Scwagner
1
Wenn Sie den .NET-Code von ReadOnlyObservableCollectionIhnen durchsuchen, finden Sie Folgendes : event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged. Explizite Schnittstellenimplementierung des Ereignisses.
Mike de Klerk
6

Ich weiß, dass dieser Beitrag alt ist, aber die Leute sollten sich Zeit nehmen, um die in .NET verwendeten Muster zu verstehen, bevor sie Kommentare abgeben. Eine schreibgeschützte Sammlung ist ein Wrapper für eine vorhandene Sammlung, der verhindert, dass Verbraucher sie direkt ändern. Sehen ReadOnlyCollectionSie sich an, und Sie werden sehen, dass es sich um einen Wrapper handelt, IList<T>der möglicherweise veränderbar ist oder nicht. Unveränderliche Sammlungen sind eine andere Sache und werden von der neuen Bibliothek unveränderlicher Sammlungen abgedeckt

Mit anderen Worten, schreibgeschützt ist nicht dasselbe wie unveränderlich !!!!

Davon abgesehen ReadOnlyObservableCollectionsollte implizit umgesetzt werden INotifyCollectionChanged.

Jason Young
quelle
5

Es gibt definitiv gute Gründe, Benachrichtigungen über geänderte Sammlungen in einer ReadOnlyObservableCollection abonnieren zu wollen . So, als Alternative zu lediglich Ihrer Sammlung als Casting INotifyCollectionChanged , wenn Sie geschehen , werden Subklassen ReadOnlyObservableCollection , dann bietet die folgenden eine syntaktisch bequeme Möglichkeit , die einen Zugriff auf Collection Ereignis:

    public class ReadOnlyObservableCollectionWithCollectionChangeNotifications<T> : ReadOnlyObservableCollection<T>
{
    public ReadOnlyObservableCollectionWithCollectionChangeNotifications(ObservableCollection<T> list)
        : base(list)
    {
    }

    event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged2
    {
        add { CollectionChanged += value; }
        remove { CollectionChanged -= value; }
    }
}

Das hat bei mir schon mal gut geklappt.

porcus
quelle
5

Sie können für den Fehlereintrag in Microsoft Connect stimmen, der dieses Problem beschreibt: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

Aktualisieren:

Das Connect-Portal wurde von Microsoft heruntergefahren. Der obige Link funktioniert also nicht mehr.

Meine WAF-Bibliothek (Win Application Framework) bietet eine Lösung: ReadOnlyObservableList- Klasse:

public class ReadOnlyObservableList<T> 
        : ReadOnlyObservableCollection<T>, IReadOnlyObservableList<T>
{
    public ReadOnlyObservableList(ObservableCollection<T> list)
        : base(list)
    {
    }

    public new event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add { base.CollectionChanged += value; }
        remove { base.CollectionChanged -= value; }
    }

    public new event PropertyChangedEventHandler PropertyChanged
    {
        add { base.PropertyChanged += value; }
        remove { base.PropertyChanged -= value; }
    }
}
jbe
quelle
Connect wurde eingestellt. Ich hätte total abgestimmt
findusl
1

Wie bereits beantwortet, haben Sie zwei Möglichkeiten: Sie können entweder ReadOnlyObservableCollection<T>die Schnittstelle in die Schnittstelle umwandeln, INotifyCollectionChangedum auf das explizit implementierte CollectionChangedEreignis zuzugreifen , oder Sie können eine eigene Wrapper-Klasse erstellen, die dies einmal im Konstruktor ausführt und nur die Ereignisse des Wraps verknüpft ReadOnlyObservableCollection<T>.

Einige zusätzliche Einblicke, warum dieses Problem noch nicht behoben wurde:

Wie Sie dem Quellcode entnehmen können , ReadOnlyObservableCollection<T>handelt es sich um eine öffentliche, nicht versiegelte (dh vererbbare) Klasse, in der die Ereignisse markiert sind protected virtual.

Das heißt, es gibt möglicherweise kompilierte Programme mit Klassen, von denen abgeleitet wurde ReadOnlyObservableCollection<T>, mit überschriebenen Ereignisdefinitionen, aber protectedSichtbarkeit. Diese Programme würden ungültigen Code enthalten, sobald die publicSichtbarkeit des Ereignisses in der Basisklasse geändert wird , da die Sichtbarkeit eines Ereignisses in abgeleiteten Klassen nicht eingeschränkt werden darf.

Leider ist das spätere Erstellen von protected virtualEreignissen publiceine binäre Änderung, und daher wird dies nicht ohne sehr gute Argumentation geschehen, was ich befürchte, "Ich muss das Objekt einmal umsetzen, um Handler anzuhängen", einfach nicht.

Quelle: GitHub-Kommentar von Nick Guererra, 19. August 2015

LWChris
quelle
0

Dies war der Top-Hit bei Google, also dachte ich mir, ich würde meine Lösung hinzufügen, falls andere Leute dies nachschlagen.

Unter Verwendung der obigen Informationen (über die Notwendigkeit, in INotifyCollectionChanged umzuwandeln ) habe ich zwei Erweiterungsmethoden zum Registrieren und Aufheben der Registrierung erstellt.

Meine Lösung - Erweiterungsmethoden

public static void RegisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged += handler;
}

public static void UnregisterCollectionChanged(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
{
    collection.CollectionChanged -= handler;
}

Beispiel

IThing.cs

public interface IThing
{
    string Name { get; }
    ReadOnlyObservableCollection<int> Values { get; }
}

Verwenden der Erweiterungsmethoden

public void AddThing(IThing thing)
{
    //...
    thing.Values.RegisterCollectionChanged(this.HandleThingCollectionChanged);
}

public void RemoveThing(IThing thing)
{
    //...
    thing.Values.UnregisterCollectionChanged(this.HandleThingCollectionChanged);
}

OPs Lösung

public void AddThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    INotifyCollectionChanged thingCollection = thing.Values;
    thingCollection.CollectionChanged -= this.HandleThingCollectionChanged;
}

Alternative 2

public void AddThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged += this.HandleThingCollectionChanged;
}

public void RemoveThing(IThing thing)
{
    //...
    (thing.Values as INotifyCollectionChanged).CollectionChanged -= this.HandleThingCollectionChanged;
}
Dan
quelle