为什么ReadOnlyObservableCollection.CollectionChanged不公开?


86

为什么要ReadOnlyObservableCollection.CollectionChanged受保护而不是公开(与之相对应ObservableCollection.CollectionChanged)?

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


1
出于好奇,为什么您会期望只读集合发生变化?当然可以,它不会是只读的吗?
workmad3

83
反问:为什么我不希望ObservableCollection发生变化?如果什么都不会改变,观察它有什么用?好吧,收藏肯定会发生变化,但是我有一些消费者只能观察它。看起来但没有感人……
Oskar 2010年

1
我最近也遇到了这个问题。基本上,ObservableCollection无法正确实现INotifyCollection更改事件。为什么C#允许类限制访问接口事件而不限制接口方法?
djskinner 2010年

35
我必须为这完全是疯狂而投我的票。如果您不能订阅CollectionChanged事件,为什么世界上甚至还存在ReadOnlyObservableCollection?WTF是重点吗?对于一直说只读集合永远不会改变的每个人,请认真思考您在说什么。
MojoFilter 2010年

9
某些人似乎认为“只读”并不意味着“不变”。这仅意味着只允许通过此类属性查看集合的代码无法对其进行更改。当代码通过基础集合添加或删除成员时,确实可以更改它。即使他们自己不能更改收藏集,也应该仍然能够通知读者发生了更改。他们可能仍然需要自己应对变化。我可以认为没有合理的理由来限制CollectionChanged属性,就像在这种情况下所做的那样。
吉尔(Gil)

Answers:



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
浏览您的.Net代码ReadOnlyObservableCollection会发现:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged。事件的显式接口实现。
Mike de Klerk

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

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

更新:

Microsoft已关闭Connect门户。因此,上面的链接不再起作用。

我的Win应用程序框架(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

1

正如已经回答的那样,您有两个选择:可以将强制ReadOnlyObservableCollection<T>转换为接口INotifyCollectionChanged以访问显式实现的CollectionChanged事件,或者可以创建自己的包装器类,该包装器类在构造函数中执行一次操作,并仅连接包装好的事件ReadOnlyObservableCollection<T>

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

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

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

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

来源:Nick Guererra的GitHub评论,2015年8月19日


0

这在Google上很受欢迎,所以我想我会添加解决方案,以防其他人查到。

使用上面的信息(有关需要转换为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;
}

物联网

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

说明

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

这是一个这样的场景:

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

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

现在,假设您要在内部集合更改时(因此在公开的ReadOnlyObservableCollection更改时)通知服务的使用者。不必滚动自己的实现,而只希望公开CollectionChangedReadOnlyObservableCollection。不必强迫使用者对的实现做一个假设ReadOnlyObservableCollection,您只需将它ReadOnlyObservableCollection与该自定义交换ObservableReadOnlyCollection,就可以了。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.