ReadOnlyObservableCollection.CollectionChangedがパブリックでないのはなぜですか?


86

なぜReadOnlyObservableCollection.CollectionChanged保護されて公開されていないのObservableCollection.CollectionChangedですか(対応するもののように)?

イベントにINotifyCollectionChangedアクセスできない場合、コレクションを実装するとCollectionChangedどうなりますか?


1
好奇心から、なぜあなたは、読み取り専用のコレクションを期待されるだろう変更?確かにそれならそれは読み取り専用ではないでしょうか?
workmad3 2010年

83
反対の質問:ObservableCollectionが変更されることを期待しないのはなぜですか?何も変わらない場合、それを観察することの使用は何ですか?さて、コレクションは間違いなく変わるでしょうが、私にはそれを観察することしか許されていない消費者がいます。探しませんが、何の感動...
オスカー

1
最近、この問題にも遭遇しました。基本的に、ObservableCollectionはINotifyCollectionの変更されたイベントを適切に実装していません。C#でクラスがアクセスインターフェイスイベントを制限できるが、インターフェイスメソッドは制限できないのはなぜですか?
djskinner 2010年

35
私はこれが完全な狂気であることに投票しなければなりません。CollectionChangedイベントをサブスクライブできない場合でも、ReadOnlyObservableCollectionが存在するのはなぜですか?WTFがポイントですか?そして、読み取り専用のコレクションは決して変わらないと言い続けるすべての人に、あなたが言っていることについて本当に真剣に考えてください。
MojoFilter 2010年

9
「読み取り専用」は、一部の人が考えているように「不変」を意味するものではありません。これは、そのようなプロパティを介してのみコレクションを表示できるコードがコレクションを変更できないことを意味するだけです。コードが基になるコレクションを介してメンバーを追加または削除するときに、実際に変更できます。コレクションを自分で変更できない場合でも、読者は変更が発生したことを通知できるはずです。彼らはまだ彼ら自身の変化に対応する必要があるかもしれません。この場合に行われたように、CollectionChangedプロパティを制限する正当な理由は考えられません。
ギル2013年

回答:



16

私はあなたのためにこれを行う方法を見つけました:

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

INotifyCollectionChangedインターフェイスによってコレクションを明示的に参照する必要があります。


14
ReadOnlyObservableCollectionはObservableCollectionのラッパーであることに注意してください。これは変更される予定です。ReadOnlyObservableCollectionのコンシューマーは、これらの変更のみを監視でき、自分自身は何も変更できません。
オスカー2010年

この事実に頼るべきではありません。読み取り専用コレクションはどのような場合でも変更されません。これは「読み取り専用」と呼ばれます。これが私の理解です。
Restuta 2010年

4
鋳造液は+1。しかし、読み取り専用コレクションを監視することは論理的ではありません。データベースへの読み取り専用アクセスがあった場合、データベースが変更されることはないと思いますか?
djskinner 2010年

16
ここでの難しさはわかりません。ReadOnlyObservableCollectionは、コレクションを監視するための読み取り専用の方法を提供するクラスです。たとえば、クラスにセッションのコレクションがあり、コレクションにセッションを追加したり、コレクションからセッションを削除したりすることはできなくても、それらを監視できるようにしたくない場合は、ReadOnlyObservableCollectionがそのための最適な手段です。コレクションは私のクラスのユーザーにのみ読み取られますが、私は自分で使用するための読み取り/書き込みコピーを持っています。
scwagner 2010年

1
ReadOnlyObservableCollectionあなたの.Netコードを閲覧すると、これが見つかります:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged。イベントの明示的なインターフェース実装。
Mike de Klerk 2016年

7

この投稿は古いことは知っていますが、コメントする前に、時間をかけて.NETで使用されているパターンを理解する必要があります。読み取り専用コレクションは、既存のコレクションのラッパーであり、コンシューマーがコレクションを直接変更できないようにReadOnlyCollectionします。見てみると、変更IList<T>可能かどうかわからないラッパーであることがわかります。不変のコレクションは別の問題であり、新しい不変のコレクションライブラリでカバーされています

言い換えれば、読み取り専用は不変と同じではありません!!!!

それはさておき、ReadOnlyObservableCollection暗黙的に実装する必要がありますINotifyCollectionChanged


5

ReadOnlyObservableCollectionでコレクション変更通知をサブスクライブするのには間違いなく正当な理由があります。したがって、単にコレクションをINotifyCollectionChangedとしてキャストする代わりに、ReadOnlyObservableCollectionをサブクラス化する場合、以下は、CollectionChangedイベントにアクセスするためのより構文的に便利な方法を提供します。

    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; }
    }
}

これは以前私にとってはうまくいきました。


5

この問題を説明するMicrosoftConnectのバグエントリに投票することができます:https//connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

更新:

ConnectポータルはMicrosoftによってシャットダウンされました。したがって、上記のリンクは機能しなくなります。

My Win Application Framework(WAF)ライブラリは、次のソリューションを提供します。ReadOnlyObservableListクラス:

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; }
    }
}

Connectは廃止されました。私は完全に投票したでしょう
findusl 2018年

1

すでに回答したように、2つのオプションがあります。明示的に実装されたイベントにアクセスReadOnlyObservableCollection<T>するためにインターフェイスINotifyCollectionChangedにキャストするかCollectionChanged、コンストラクターで一度それを実行し、ラップされたのイベントをフックするだけの独自のラッパークラスを作成できますReadOnlyObservableCollection<T>

この問題がまだ修正されていない理由に関するいくつかの追加の洞察:

あなたから見ることができるように、ソースコードReadOnlyObservableCollection<T>イベントがマークされている公共、非密封された(すなわち継承)クラス、ですprotected virtual

つまり、から派生したクラスを使用してコンパイルされたプログラムがありReadOnlyObservableCollection<T>、イベント定義はオーバーライドされますが、protected可視性があります。public派生クラスでのイベントの可視性を制限することは許可されていないため、基本クラスでイベントの可視性が変更されると、これらのプログラムには無効なコードが含まれます。

残念ながら、後でprotected virtualイベントを作成することpublicはバイナリを壊す変更であり、したがって、「ハンドラーをアタッチするためにオブジェクトを1回キャストする必要がある」という非常に正当な理由がなければ、それは実行されません。

出典:Nick GuererraによるGitHubのコメント、2015年8月19日


0

これはグーグルでトップヒットだったので、他の人がこれを調べた場合に備えて自分のソリューションを追加すると思いました。

上記の情報(INotifyCollectionChangedへのキャストの必要性について)を使用して、登録と登録解除の2つの拡張メソッドを作成しました。

私の解決策-拡張メソッド

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

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

IThing.cs

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

拡張メソッドの使用

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

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

OPのソリューション

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;
}

代替案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;
}

0

解決

ReadOnlyObservableCollection.CollectionChanged (他の回答で概説されている正当な理由のために)公開されていないので、それを公開する独自のラッパークラスを作成しましょう:

/// <summary>A wrapped <see cref="ReadOnlyObservableCollection{T}"/> that exposes the internal <see cref="CollectionChanged"/>"/>.</summary>
public class ObservableReadOnlyCollection<T> : ReadOnlyObservableCollection<T>
{
    public new NotifyCollectionChangedEventHandler CollectionChanged;

    public ObservableReadOnlyCollection(ObservableCollection<T> list) : base(list) { /* nada */ }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) => 
        CollectionChanged?.Invoke(this, args);
}

説明

読み取り専用コレクションへの変更を観察する理由を尋ねられたので、多くの有効な状況の1つを説明します。読み取り専用コレクションが、変更可能なプライベート内部コレクションをラップする場合。

そのようなシナリオの1つを次に示します。

サービスの外部から内部コレクションにアイテムを追加および削除できるサービスがあるとします。ここで、コレクションの値を公開したいが、コンシューマーがコレクションを直接操作したくないとします。したがって、内部コレクションをでラップしますReadOnlyObservableCollection

内部コレクションを内部コレクションでラップするために、のコンストラクターによってReadOnlyObservableCollection派生するように強制されることに注意してください。ObservableCollectionReadOnlyObservableCollection

ここで、内部コレクションが変更されたとき(したがって、公開されたReadOnlyObservableCollection変更が変更されたとき)にサービスをコンシューマーに通知するとします。むしろ独自の実装をロールよりもあなただけ公開するCollectionChangedのをReadOnlyObservableCollection。消費者にの実装についての仮定を強制するのではなく、ReadOnlyObservableCollection単にReadOnlyObservableCollectionこのカスタムと交換するだけでObservableReadOnlyCollection完了です。

ObservableReadOnlyCollection皮革ReadOnlyObservableCollection.CollectionChangedそれには自分だ、と単純に接続されているすべてのイベントハンドラにイベントを変更し、すべてのコレクションを渡します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.