为什么ReadOnlyObservableCollection.CollectionChanged不公开?

时间:2010-01-13 16:08:22

标签: c# .net collections

为什么ReadOnlyObservableCollection.CollectionChanged受到保护而不是公开(因为相应的ObservableCollection.CollectionChanged是)?

如果我无法访问INotifyCollectionChanged事件,实施CollectionChanged的集合有什么用途?

8 个答案:

答案 0 :(得分:52)

以下是解决方案:CollectionChanged events on ReadOnlyObservableCollection

您必须强制集合到 INotifyCollectionChanged

答案 1 :(得分:14)

我找到了一种方法让你知道如何做到这一点:

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

您只需要通过 INotifyCollectionChanged 界面明确引用您的收藏。

答案 2 :(得分:5)

您可以投票选择Microsoft Connect上描述此问题的错误条目:https://connect.microsoft.com/VisualStudio/feedback/details/641395/readonlyobservablecollection-t-collectionchanged-event-should-be-public

<强>更新

Microsoft已关闭Connect门户。所以上面的链接不再起作用了。

我的Win应用程序框架(WAF)库提供了一个解决方案:ReadOnlyObservableList class:

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

答案 3 :(得分: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; }
    }
}

这对我来说效果很好。

答案 4 :(得分:5)

我知道这篇文章很老,但是,在评论之前,人们应该花时间了解.NET中使用的模式。只读集合是现有集合的包装器,它阻止消费者直接修改它,查看ReadOnlyCollection,您将看到它是IList<T>的包装器,可能是也可能不是可变的。不可变集合是另一回事,并由新immutable collections library

涵盖

换句话说,只读是不可变的!!!!

除此之外,ReadOnlyObservableCollection应隐式实施INotifyCollectionChanged

答案 5 :(得分:1)

正如已经回答的那样,您有两个选择:您可以将ReadOnlyObservableCollection<T>强制转换为接口INotifyCollectionChanged来访问显式实现的CollectionChanged事件,也可以创建自己的包装器类只需在构造函数中执行一次操作,然后就将包装的ReadOnlyObservableCollection<T>的事件挂接起来。

一些其他有关为何尚未解决此问题的见解:

the source code中可以看到,ReadOnlyObservableCollection<T>是一个公共的,未密封的(即可继承的)类,其中事件标记为protected virtual

也就是说,可能存在具有从ReadOnlyObservableCollection<T>派生的类的已编译程序,这些类具有覆盖的事件定义,但可见性为protected。一旦事件的可见性在基类中更改为public,这些程序将包含无效代码,因为不允许在派生类中限制事件的可见性。

很不幸,稍后再进行protected virtual个事件public是一个破坏二进制的更改,因此,如果没有很好的推理就无法完成,恐怕“我必须强制转换对象一次附加处理程序”根本不是。

来源:GitHub comment by Nick Guererra, August 19th, 2015

答案 6 :(得分:0)

这是谷歌的热门话题,所以我想我会添加我的解决方案以防其他人查看。

使用上面的信息(关于需要转换为 INotifyCollectionChanged ),我做了两个扩展方法来注册和取消注册。

我的解决方案 - 扩展方法

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

答案 7 :(得分: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);
}

说明

人们问了为什么要观察对只读集合的​​更改,因此,我将解释许多有效情况之一。当只读集合包装可以更改的私有内部集合时。

这是一个这样的场景:

假设您有一项服务,该服务允许从服务外部向内部集合添加和删除项目。现在,假设您要公开集合的值,但又不希望使用者直接操作集合。因此您可以将内部集合包装在ReadOnlyObservableCollection中。

请注意,为了用ReadOnlyObservableCollection包装内部集合,内部集合必须由ObservableCollection的构造函数从ReadOnlyObservableCollection派生。

现在,假设您要在内部集合更改时(因此在公开的ReadOnlyObservableCollection更改时)将服务通知给使用者。与其发布自己的实现,不如只公开CollectionChanged中的ReadOnlyObservableCollection。不必强迫消费者对ReadOnlyObservableCollection的实现做一个假设,您只需将ReadOnlyObservableCollection与此自定义ObservableReadOnlyCollection交换,就可以了。

ObservableReadOnlyCollection自身隐藏了ReadOnlyObservableCollection.CollectionChanged,并简单地将所有已更改集合的事件传递给任何附加的事件处理程序。