首页 > 解决方案 > 当听众退订时,我可以收到通知吗?

问题描述

概括

  1. 我有一个简单的类,它公开了 type 的属性ReadOnlyObservableCollection
  2. 第一次访问此属性时,会生成相当多的数据。
  3. 生成的数据是时间相关的,因此会随着时间而改变——因此需要属性是 type ReadOnlyObservableCollection
  4. 这按预期工作,但是生成的数据集足够大,可以保证在应用程序的整个生命周期内我会遇到内存问题。

问题

我需要能够删除对生成数据的引用,以便 GC 可以收集这些数据。ReadOnlyObservableCollection但问题是,除非我知道- 或者更准确地说是底层INotifyCollectionChanged.CollectionChanged事件没有侦听器,否则我不能这样做。

当侦听器取消订阅事件而不实现所述事件时,有没有一种方法可以轻松地通知我?

一种相当简单但可能有点笨拙的解决方法是跟踪所有具有活动数据的 Storage 对象,CleanDataSet如果没有INotifyCollectionChanged.CollectionChanged事件的侦听器,则每隔一段时间调用该方法。

示例代码

public class Storage
{

    //The regular computation of the data set is not shown as it is not relevant
    //to the issue at hand.

    public ReadOnlyObservableCollection<String> Data
    {
        get
        {
            if (DataList == null)
            {
                DataList = new ObservableCollection<String>();
                DataReadOnly = new ReadOnlyObservableCollection<String>(DataList);
                ComputeDataSet();
                //I require a way of 'listening' when the
                //DataList.CollectionChanged gains or loses a subscriber.
            }
            return DataReadOnly;
        }
    }

    private ReadOnlyObservableCollection<String> DataReadOnly;
    private ObservableCollection<String> DataList;

    private void ComputeDataSet()
    {
        Random random = new Random();
        for (Int32 counter = 0; counter < 10000; counter++)
            DataList.Add(random.Next().ToString());
    }

    private void CleanDataSet()
    {
        DataReadOnly = null;
        DataList = null;
    }

}

标签: c#events

解决方案


ReadOnlyObservableCollection.CollectionChanged您可以使用虚拟的事实,null如果没有订阅者,则返回事件:

using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
                    
public class Program
{
    public static void Main()
    {
        var innerCollection = new ObservableCollection<string>() { "foo", "bar" };
        var collection = (INotifyCollectionChanged)new MyReadOnlyObservableCollection<string>(innerCollection);
        
        collection.CollectionChanged += Handler;
        
        innerCollection.Add("baz");
        
        collection.CollectionChanged -= Handler;
    }
    
    private static void Handler(object sender, NotifyCollectionChangedEventArgs e)
    {
        Console.WriteLine(e.NewItems[0]);   
    }
}

public class MyReadOnlyObservableCollection<T> : ReadOnlyObservableCollection<T>
{
    public MyReadOnlyObservableCollection(ObservableCollection<T> list) : base(list) { }
    
    private event NotifyCollectionChangedEventHandler InnerEvent;
    
    protected override event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add
        {
            if (InnerEvent == null)
            {
                Console.WriteLine("Got our first subscriber");
            }
            InnerEvent += value;
        }
        
        remove
        {
            InnerEvent -= value;
            if (InnerEvent == null)
            {
                Console.WriteLine("There are no more subscribers");
            }
        }
    }
    
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
    {
        InnerEvent?.Invoke(this, args);
    }
}

我们覆盖CollectionChanged事件,并提供我们自己的添加和删除处理程序。这些处理程序委托给内部事件(我们可以在这里使用委托,但事件+=是线程安全的,而委托不是)。在 remove 处理程序中,我们还测试内部事件是否为null: 如果是,则不再有订阅者。

我们还需要重写OnCollectionChanged来调用我们的内部事件。

然而,此时我们几乎重写了ReadOnlyObservableCollection:它不是一个大类


推荐阅读