ReadOnlyObservableCollection.CollectionChanged neden herkese açık değil?


88

Neden ReadOnlyObservableCollection.CollectionChangedkorunuyor ve kamuya açık değil (karşılık gelen gibi ObservableCollection.CollectionChanged)?

Etkinliğe INotifyCollectionChangederişemezsem bir koleksiyonun uygulanması ne işe yarar CollectionChanged?


1
Sadece meraktan, neden salt okunur koleksiyon bekliyor olacak kadar değişim? Elbette o zaman sadece okunmaz mı?
workmad3

84
Karşı soru: Bir ObservableCollection'ın değişmesini neden beklemeyeyim? Hiçbir şey değişmeyecekse onu gözlemlemenin ne anlamı var? Koleksiyon kesinlikle değişecek, ancak sadece onu gözlemleyebilecek tüketicilerim var. Bakıyor ama dokunmak yok ...
Oskar

1
Geçenlerde bu konuyla da karşılaştım. Temel olarak ObservableCollection, INotifyCollection değiştirilen olayını doğru şekilde uygulamıyor. Neden C # bir sınıfa erişim arabirimi olaylarını kısıtlama izni verirken arabirim yöntemlerini kısıtlamaz?
djskinner

36
Bunun tam bir delilik olduğu için oy kullanmalıyım. CollectionChanged etkinliklerine abone olamıyorsanız bile ReadOnlyObservableCollection neden dünyada var? Konu WTF mi? Ve salt okunur bir koleksiyonun asla değişmeyeceğini söyleyen herkese, söyledikleriniz hakkında gerçekten çok düşünün.
MojoFilter

9
Bazılarının düşündüğü gibi "salt okunur" "değişmez" anlamına gelmez. Yalnızca, koleksiyonu yalnızca böyle bir özellik aracılığıyla görebilen kodun onu değiştirmesine izin verilmediği anlamına gelir. Kod, temel koleksiyon aracılığıyla üye eklediğinde veya kaldırdığında gerçekten değiştirilebilir. Okuyucular, koleksiyonu kendileri değiştiremeyecek olsalar bile, değişikliklerin olduğu konusunda yine de bilgilendirilebilmelidir. Yine de değişikliklere kendileri yanıt vermeleri gerekebilir. Bu durumda yapıldığı gibi CollectionChanged özelliğini kısıtlamak için geçerli bir neden düşünemiyorum.
Gil

Yanıtlar:



16

Bunu nasıl yapacağına dair sana bir yol buldum:

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

Koleksiyonunuza INotifyCollectionChanged arayüzü ile açıkça başvurmanız yeterlidir .


14
Lütfen ReadOnlyObservableCollection'ın bir ObservableCollection etrafında bir sarmalayıcı olduğunu unutmayın - bu değişecek. ReadOnlyObservableCollection tüketicilerinin yalnızca bu değişiklikleri gözlemlemesine izin verilir, hiçbir şeyi kendileri değiştiremez.
Oskar

Bu gerçeğe güvenmemelisiniz, salt okunur koleksiyon hiçbir durumda değişmeyecek, çünkü buna "salt okunur" deniyor. Bu benim anlayışım.
Restuta

4
Döküm çözümü için +1. Ancak, salt okunur bir koleksiyonu gözlemlemenin mantıklı olmamasına gelince: Bir veritabanına salt okuma erişiminiz olsaydı, onun asla değişmemesini bekler miydiniz?
djskinner

17
Buradaki zorluğun ne olduğunu anlamıyorum. ReadOnlyObservableCollection, bir koleksiyonu gözlemlemek için SADECE OKUNAN bir yol sağlayan bir sınıftır. Sınıfımda oturumlardan oluşan bir koleksiyon varsa ve insanların koleksiyonumdan oturumlar ekleyip çıkarmasını istemiyorsam, ancak yine de onları gözlemliyorsam, ReadOnlyObservableCollection bunun için mükemmel bir araçtır. Koleksiyon sadece sınıfımın kullanıcıları için okunur, ancak kendi kullanımım için bir okuma / yazma kopyasına sahibim.
scwagner

1
Arasında .Net kodunu tarama ReadOnlyObservableCollectionsize bu bulacaksınız: event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged. Etkinliğin açık arayüz uygulaması.
Mike de Klerk

7

Bu yazının eski olduğunu biliyorum, ancak insanlar yorum yapmadan önce .NET'te kullanılan kalıpları anlamak için zaman ayırmalıdır. Salt okunur bir koleksiyon, tüketicilerin onu doğrudan değiştirmesini engelleyen, mevcut bir koleksiyonda ReadOnlyCollectionbulunan bir sarmalayıcıdır IList<T>. Değişmez koleksiyonlar farklı bir konudur ve yeni değişmez koleksiyon kitaplığı tarafından kapsanmaktadır

Diğer bir deyişle, salt okunur, değişmez ile aynı şey değildir !!!!

Bu bir yana, ReadOnlyObservableCollectiondolaylı olarak uygulanmalıdır INotifyCollectionChanged.


5

ReadOnlyObservableCollection'da koleksiyon değişikliği bildirimlerine abone olmayı istemenin kesinlikle iyi nedenleri vardır . Dolayısıyla, koleksiyonunuzu yalnızca INotifyCollectionChanged olarak yayınlamaya bir alternatif olarak , ReadOnlyObservableCollection alt sınıflara ayırıyorsanız , aşağıdaki yöntem, CollectionChanged olayına erişmek için daha sözdizimsel olarak daha uygun bir yol sağlar :

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

Bu benim için daha önce iyi çalıştı.


5

Microsoft Connect'te bu sorunu açıklayan hata girişine oy verebilirsiniz: https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

Güncelleme:

Connect portalı Microsoft tarafından kapatılmıştır. Yani yukarıdaki bağlantı artık çalışmıyor.

Win Application Framework (WAF) kitaplığım bir çözüm sağlar: ReadOnlyObservableList sınıfı:

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 kullanımdan kaldırıldı. Ben tamamen
oylardım

1

Zaten yanıtlandığı gibi, iki seçeneğiniz vardır: ya açıkça uygulanan olaya erişmek ReadOnlyObservableCollection<T>için arabirime INotifyCollectionChangeddönüştürebilirsiniz CollectionChangedya da bunu yapıcıda bir kez yapan ve yalnızca sarmalanmış olayları bağlayan kendi sarmalayıcı sınıfınızı oluşturabilirsiniz ReadOnlyObservableCollection<T>.

Bu sorunun neden henüz düzeltilmediğine dair bazı ek bilgiler:

Eğer görebileceğiniz gibi kaynak kodu , ReadOnlyObservableCollection<T>olaylar işaretlenmiştir kamu, sivil mühürlü (yani kalıtımsal) sınıfı vardır protected virtual.

Yani, ReadOnlyObservableCollection<T>geçersiz kılınmış olay tanımları ancak protectedgörünürlüğü olan, türetilen sınıflara sahip derlenmiş programlar olabilir . publicTüretilmiş sınıflarda bir olayın görünürlüğünü kısıtlamaya izin verilmediğinden , olayın görünürlüğü temel sınıfta değiştirildiğinde bu programlar geçersiz kod içerir .

Bu yüzden ne yazık ki, protected virtualolayları publicdaha sonra yapmak iki parçalı bir değişikliktir ve bu nedenle çok iyi bir mantık olmadan yapılmayacaktır, korkarım "işleyicileri eklemek için nesneyi bir kez atmam gerekiyor" basitçe değildir.

Kaynak: GitHub yorumu Nick Guererra, 19 Ağustos 2015


0

Bu, Google'da en çok hit oldu, bu yüzden başka insanların buna bakması durumunda çözümümü ekleyeceğimi düşündüm.

Yukarıdaki bilgileri kullanarak ( INotifyCollectionChanged'ye çevirme ihtiyacı hakkında ), kaydolmak ve kaydı silmek için iki uzantı yöntemi yaptım.

Çözümüm - Uzatma Yöntemleri

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

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

Misal

IThing.cs

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

Uzatma Yöntemlerini Kullanma

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

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

OP'nin Çözümü

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

0

Çözüm

ReadOnlyObservableCollection.CollectionChanged açığa çıkmaz (diğer yanıtlarda belirtilen geçerli nedenlerden dolayı), öyleyse onu açığa çıkaran kendi sarmalayıcı sınıfımızı yapalım:

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

Açıklama

İnsanlar salt okunur bir koleksiyondaki değişiklikleri neden gözlemlemek istediğinizi sordular, bu yüzden birçok geçerli durumdan birini açıklayacağım; salt okunur koleksiyon, değişebilen özel bir dahili koleksiyonu sarmaladığında.

İşte böyle bir senaryo:

Hizmetin dışından bir iç koleksiyona öğe eklemeye ve kaldırmaya izin veren bir hizmetiniz olduğunu varsayalım. Şimdi, koleksiyonun değerlerini ortaya çıkarmak istediğinizi, ancak tüketicilerin koleksiyonu doğrudan değiştirmesini istemediğinizi varsayalım; böylece iç koleksiyonu bir ReadOnlyObservableCollection.

Dahili koleksiyonu iç koleksiyonla sarmak için , yapıcısı tarafından ReadOnlyObservableCollectiontüretilmeye zorlanır .ObservableCollectionReadOnlyObservableCollection

Şimdi, dahili koleksiyon değiştiğinde (ve dolayısıyla maruz kalan ReadOnlyObservableCollectiondeğişiklik olduğunda ) tüketicileri hizmet hakkında bilgilendirmek istediğinizi varsayalım . Kendi uygulamanızı devirmek yerine CollectionChanged, yalnızca ReadOnlyObservableCollection. Tüketiciyi ürünün uygulanması hakkında bir varsayım yapmaya zorlamak yerine ReadOnlyObservableCollection, sadece ReadOnlyObservableCollectionbu gelenekle değiştirirsiniz ObservableReadOnlyCollectionve bitirdiniz.

ObservableReadOnlyCollectionGizler ReadOnlyObservableCollection.CollectionChangedonunla kendi var ve sadece herhangi ekli olay işleyicisi için tüm koleksiyon değiştirildi olaylara geçer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.