Mengapa ReadOnlyObservableCollection.CollectionChanged tidak bersifat publik?

88

Mengapa ReadOnlyObservableCollection.CollectionChangeddilindungi dan tidak bersifat publik (sebagaimana yang sesuai ObservableCollection.CollectionChanged)?

Apa gunanya implementasi collection INotifyCollectionChangedjika saya tidak dapat mengakses CollectionChangedacara?

Oskar
sumber
1
Karena penasaran, mengapa Anda akan mengharapkan koleksi read-only untuk perubahan? Tentunya itu tidak akan bisa dibaca saja?
workmad3
84
Pertanyaan balasan: Mengapa saya tidak mengharapkan ObservableCollection berubah? Apa gunanya mengamati jika tidak ada yang akan berubah? Memang koleksinya pasti akan berubah, tapi saya punya konsumen yang hanya boleh mengamatinya. Melihat tapi tidak menyentuh ...
Oskar
1
Saya baru-baru ini juga menemukan masalah ini. Pada dasarnya ObservableCollection tidak mengimplementasikan peristiwa INotifyCollection yang diubah dengan benar. Mengapa C # memungkinkan kelas untuk membatasi peristiwa antarmuka akses tetapi tidak metode antarmuka?
djskinner
36
Saya harus memberikan suara saya karena ini adalah kegilaan total. Mengapa ReadOnlyObservableCollection ada di dunia ini jika Anda tidak dapat berlangganan acara CollectionChanged? WTF intinya? Dan untuk semua orang yang terus mengatakan bahwa koleksi hanya-baca tidak akan pernah berubah, pikirkan baik-baik apa yang Anda katakan.
MojoFilter
9
"hanya baca" tidak berarti "tidak berubah" seperti yang dipikirkan beberapa orang. Ini hanya berarti bahwa kode yang hanya dapat melihat koleksi melalui properti semacam itu tidak diizinkan untuk mengubahnya. Itu memang dapat diubah ketika kode menambah atau menghapus anggota melalui koleksi yang mendasarinya. Pembaca tetap dapat diberi tahu bahwa telah terjadi perubahan, meskipun mereka tidak dapat mengubah koleksinya sendiri. Mereka mungkin masih perlu menanggapi perubahan itu sendiri. Saya tidak dapat memikirkan alasan yang sah untuk membatasi properti CollectionChanged seperti yang telah dilakukan dalam kasus ini.
Gil

Jawaban:

16

Saya telah menemukan cara bagi Anda untuk melakukan ini:

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

Anda hanya perlu merujuk ke koleksi Anda secara eksplisit dengan antarmuka INotifyCollectionChanged .

Restuta
sumber
14
Harap diperhatikan bahwa ReadOnlyObservableCollection adalah pembungkus di sekitar ObservableCollection - yang akan berubah. Konsumen ReadOnlyObservableCollection hanya diizinkan untuk mengamati perubahan ini, tidak mengubah apa pun sendiri.
Oskar
Anda tidak boleh mengandalkan fakta ini, koleksi hanya-baca tidak akan berubah dalam hal apa pun, karena ini disebut "hanya-baca". Ini pemahaman saya.
Restuta
4
1 untuk solusi pengecoran. Tetapi karena tidak logis untuk mengamati koleksi hanya-baca: Jika Anda memiliki akses hanya-baca ke database, apakah Anda mengharapkannya untuk tidak pernah berubah?
djskinner
17
Saya tidak melihat kesulitan apa di sini. ReadOnlyObservableCollection adalah kelas yang menyediakan cara HANYA BACA untuk mengamati koleksi. Jika kelas saya memiliki kumpulan, katakanlah, sesi, dan saya tidak ingin orang dapat menambah atau menghapus sesi dari koleksi saya, tetapi masih mengamatinya, maka ReadOnlyObservableCollection adalah sarana yang tepat untuk itu. Koleksinya hanya dapat dibaca oleh pengguna di kelas saya, tetapi saya memiliki salinan baca / tulis untuk saya gunakan sendiri.
scwagner
1
Browsing Net kode ReadOnlyObservableCollectionAnda akan menemukan ini: event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged. Implementasi antarmuka eksplisit dari acara tersebut.
Mike de Klerk
7

Saya tahu posting ini sudah tua, namun, orang harus meluangkan waktu untuk memahami pola yang digunakan dalam .NET sebelum berkomentar. Koleksi hanya-baca adalah pembungkus pada koleksi yang ada yang mencegah konsumen untuk memodifikasinya secara langsung, lihat ReadOnlyCollectiondan Anda akan melihat bahwa itu adalah pembungkus IList<T>yang mungkin atau mungkin tidak bisa berubah. Koleksi yang tidak dapat diubah adalah masalah yang berbeda dan dilindungi oleh perpustakaan koleksi yang tidak dapat diubah yang baru

Dengan kata lain, read only tidak sama dengan immutable !!!!

Selain itu, ReadOnlyObservableCollectionharus diterapkan secara implisit INotifyCollectionChanged.

Jason Young
sumber
5

Pasti ada alasan bagus untuk ingin berlangganan notifikasi koleksi yang diubah di ReadOnlyObservableCollection . Jadi, sebagai alternatif untuk hanya mentransmisikan koleksi Anda sebagai INotifyCollectionChanged , jika Anda kebetulan menjadi subclass ReadOnlyObservableCollection , berikut ini menyediakan cara yang lebih nyaman secara sintaksis untuk mengakses acara 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; }
    }
}

Ini telah bekerja dengan baik untuk saya sebelumnya.

porcus
sumber
5

Anda dapat memilih entri bug di Microsoft Connect yang menjelaskan masalah ini: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

Memperbarui:

Portal Connect telah ditutup oleh Microsoft. Jadi link di atas tidak berfungsi lagi.

Perpustakaan My Win Application Framework (WAF) menyediakan solusi: kelas 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; }
    }
}
jbe
sumber
Connect telah dihentikan. Saya akan memilih secara total
findusl
1

Seperti yang sudah dijawab, Anda memiliki dua opsi: Anda dapat mentransmisikan ReadOnlyObservableCollection<T>ke antarmuka INotifyCollectionChangeduntuk mengakses CollectionChangedperistiwa yang diimplementasikan secara eksplisit , atau Anda dapat membuat kelas pembungkus Anda sendiri yang melakukannya sekali di konstruktor dan hanya menghubungkan peristiwa yang dibungkus ReadOnlyObservableCollection<T>.

Beberapa wawasan tambahan tentang mengapa masalah ini belum diperbaiki:

Seperti yang Anda lihat dari kode sumber , ReadOnlyObservableCollection<T>adalah publik, kelas non-tertutup (yaitu mewarisi), di mana peristiwa ditandai protected virtual.

Artinya, mungkin ada program yang dikompilasi dengan kelas yang diturunkan dari ReadOnlyObservableCollection<T>, dengan definisi acara yang diganti tetapi protectedvisibilitas. Program tersebut akan berisi kode yang tidak valid setelah visibilitas acara diubah menjadi publicdi kelas dasar, karena tidak diizinkan untuk membatasi visibilitas acara di kelas turunan.

Sungguh sayang, bikin protected virtualacarapublic kemudian hari adalah perubahan yang menghancurkan biner, dan karenanya tidak akan dilakukan tanpa alasan yang sangat baik, yang saya khawatirkan "Saya harus melemparkan objek sekali untuk memasang penangan" tidak.

Sumber: Komentar GitHub oleh Nick Guererra, 19 Agustus 2015

LWChris
sumber
0

Ini adalah hit teratas di google jadi saya pikir saya akan menambahkan solusi saya jika orang lain mencari ini.

Menggunakan informasi di atas (tentang perlunya mentransmisikan ke INotifyCollectionChanged ), saya membuat dua metode ekstensi untuk mendaftar dan membatalkan pendaftaran.

Solusi Saya - Metode Ekstensi

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

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

Contoh

IThing.cs

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

Menggunakan Metode Ekstensi

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

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

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

Alternatif 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
sumber
0

Larutan

ReadOnlyObservableCollection.CollectionChanged tidak terekspos (untuk alasan valid yang diuraikan dalam jawaban lain), jadi mari buat kelas pembungkus kita sendiri yang memaparkannya:

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

Penjelasan

Orang-orang bertanya mengapa Anda ingin mengamati perubahan pada koleksi hanya-baca, jadi saya akan menjelaskan salah satu dari banyak situasi yang valid; saat koleksi hanya-baca membungkus koleksi internal pribadi yang bisa berubah.

Inilah salah satu skenario tersebut:

Misalkan Anda memiliki layanan yang memungkinkan penambahan dan penghapusan item ke koleksi internal dari luar layanan. Sekarang misalnya Anda ingin mengekspos nilai-nilai koleksi tetapi Anda tidak ingin konsumen memanipulasi koleksi secara langsung; jadi Anda membungkus koleksi internal dalam file ReadOnlyObservableCollection.

Perhatikan bahwa untuk membungkus koleksi internal dengan ReadOnlyObservableCollectionkoleksi internal dipaksa untuk diturunkan dari ObservableCollectionoleh konstruktor ReadOnlyObservableCollection.

Sekarang misalkan Anda ingin memberi tahu konsumen tentang layanan saat koleksi internal berubah (dan karenanya saat terekspos ReadOnlyObservableCollectionberubah). Daripada menjalankan implementasi Anda sendiri, Anda hanya ingin mengekspos CollectionChangedfile ReadOnlyObservableCollection. Daripada memaksa konsumen untuk membuat asumsi tentang implementasi ReadOnlyObservableCollection, Anda cukup menukar ReadOnlyObservableCollectiondengan kebiasaan ini ObservableReadOnlyCollection, dan selesai.

The ObservableReadOnlyCollectionmenyembunyikan ReadOnlyObservableCollection.CollectionChangeddengan itu sendiri, dan hanya melewati semua peristiwa berubah koleksi untuk setiap event terlampir.

Zodman
sumber