【问题标题】:Why is ReadOnlyObservableCollection.CollectionChanged not public?为什么 ReadOnlyObservableCollection.CollectionChanged 不公开?
【发布时间】:2011-01-04 17:11:45
【问题描述】:

为什么ReadOnlyObservableCollection.CollectionChanged 是受保护的而不是公开的(就像对应的ObservableCollection.CollectionChanged 一样)?

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

【问题讨论】:

  • 出于好奇,您为什么会期待只读集合 to 发生变化?那么它肯定不会是只读的吗?
  • 反问:为什么我不期望 ObservableCollection 发生变化?如果什么都不会改变,观察它有什么用?好吧,这个系列肯定会发生变化,但我有只允许观察它的消费者。望而却步……
  • 我最近也遇到了这个问题。基本上 ObservableCollection 没有正确实现 INotifyCollection 更改事件。为什么C#允许一个类限制访问接口事件而不是接口方法?
  • 我必须投票支持这完全是精神错乱。如果您不能订阅 CollectionChanged 事件,为什么世界上还存在 ReadOnlyObservableCollection? WTF是重点?对于那些一直说只读集合永远不会改变的人,请认真思考您所说的话。
  • “只读”并不像某些人认为的那样意味着“不可变”。这只意味着只能通过这样的属性看到集合的代码不允许更改它。当代码通过基础集合添加或删除成员时,它确实可以更改。即使读者自己无法更改集合,他们仍然应该能够收到更改发生的通知。他们可能仍然需要自己响应变化。我想不出像在这种情况下那样限制 CollectionChanged 属性的正当理由。

标签: c# .net collections


【解决方案1】:

这里是解决方案:CollectionChanged events on ReadOnlyObservableCollection

您必须将集合投射INotifyCollectionChanged

【讨论】:

    【解决方案2】:

    我已经为你找到了如何做到这一点的方法:

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

    您只需要通过 INotifyCollectionChanged 接口显式引用您的集合。

    【讨论】:

    • 请不要说 ReadOnlyObservableCollection 是 ObservableCollection 的包装器 - 这将发生变化。 ReadOnlyObservableCollection 的消费者只被允许观察这些变化,而不是自己改变任何东西。
    • 你不应该依赖这个事实,只读集合无论如何都不会改变,因为它被称为“只读”。这是我的理解。
    • +1 用于铸造解决方案。但至于观察只读集合是不合逻辑的:如果您对数据库具有只读访问权限,您会期望它永远不会改变吗?
    • 我看不出这里有什么困难。 ReadOnlyObservableCollection 是一个提供只读方式来观察集合的类。如果我的班级有一个集合,比如说,会话,我不希望人们能够从我的集合中添加或删除会话,但仍然观察它们,那么 ReadOnlyObservableCollection 就是完美的工具。该集合仅供我班级的用户阅读,但我有一个读/写副本供我自己使用。
    • 浏览ReadOnlyObservableCollection的.Net代码你会发现:event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged。事件的显式接口实现。
    【解决方案3】:

    我知道这篇文章已经过时了,但是,人们在发表评论之前应该花点时间了解 .NET 中使用的模式。只读集合是现有集合的包装器,可防止消费者直接修改它,查看ReadOnlyCollection,您会看到它是IList&lt;T&gt; 的包装器,它可能是可变的,也可能不是可变的。不可变集合是另一回事,新的 immutable collections library 涵盖了这一点

    也就是说,只读不等于不可变!!!

    除此之外,ReadOnlyObservableCollection 应该隐式实现 INotifyCollectionChanged

    【讨论】:

    • 这就是问题所在,那为什么不显式实现INotifyCollectionChanged呢?
    【解决方案4】:

    想要订阅 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 Application Framework (WAF) 库提供了一个解决方案:ReadOnlyObservableListclass:

      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 已停用。我会完全投票
      【解决方案6】:

      正如已经回答的那样,您有两个选择:您可以将ReadOnlyObservableCollection&lt;T&gt; 转换为接口INotifyCollectionChanged 以访问显式实现的CollectionChanged 事件,或者您可以创建自己的包装类,在构造函数,只是连接包装的 ReadOnlyObservableCollection&lt;T&gt; 的事件。

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

      the source code 可以看出,ReadOnlyObservableCollection&lt;T&gt; 是一个公共的非密封(即可继承)类,其中的事件标记为protected virtual

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

      所以不幸的是,稍后制作protected virtual 事件public 是一个二进制破坏性更改,因此如果没有很好的理由就不会这样做,我担心“我必须强制转换对象一次才能附加处理程序" 根本不是。

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

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

          【解决方案8】:

          解决方案

          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 包装内部集合,ReadOnlyObservableCollection 的构造函数强制内部集合从ObservableCollection 派生。

          现在假设您想在内部集合更改时通知服务的使用者(因此当暴露的ReadOnlyObservableCollection 更改时)。而不是滚动您自己的实现,您只想公开ReadOnlyObservableCollectionCollectionChanged。与其强迫消费者对ReadOnlyObservableCollection 的实现做出假设,您只需将ReadOnlyObservableCollection 与此自定义ObservableReadOnlyCollection 交换,就完成了。

          ObservableReadOnlyCollectionReadOnlyObservableCollection.CollectionChanged 隐藏起来,并简单地将所有集合更改的事件传递给任何附加的事件处理程序。

          【讨论】:

          • 如果“出于其他答案中列出的正当理由”,您的意思是他们犯了一个错误并且无法修复它,因为它是一个二进制破坏修复......那么是的......有效。
          猜你喜欢
          • 2020-05-09
          • 2014-11-06
          • 1970-01-01
          • 2019-05-07
          • 1970-01-01
          • 2019-02-27
          • 2014-08-29
          • 2011-10-25
          • 2021-05-07
          相关资源
          最近更新 更多