取消订阅动态Observable

时间:2016-10-08 07:45:28

标签: c# system.reactive

我正在学习Rx-stuff并且我已经制作了一个测试应用程序,它具有共享计数器状态并动态创建计数器小部件。比如你可以按添加/删除按钮来产生/销毁一个新的计数器。它运行正常,但我不知道如何正确处理已删除的计数器小部件。

Here是我的源代码。

问题的一部分在第77行:

// update each counter's label
addCounterStream.Subscribe(x => {
    counterState.Subscribe(q => x.Counter = q.ToString()); // How can I unsubscribe later?
});

我能想到的可能的解决方案是创建一个Subject<IDisposable>来收集这些订阅,然后将此流与removeCounterStream合并,然后取消订阅。

但问题的另一部分是第40,41行:

var incFromWidget = addCounterStream.SelectMany(x => Observable.FromEventPattern(x, "Increment").Select(_ => 1));
var decFromWidget = addCounterStream.SelectMany(x => Observable.FromEventPattern(x, "Decrement").Select(_ => -1));

它合并来自所有动态创建的小部件的点击事件。如果我想删除一些小部件会发生什么?它将从表单中消失,因此无法生成新的点击事件,但它不能GC,因为它仍然保留对点击事件的引用(因为{{ 1}})。

这是内存泄漏,我是对的吗?

那么,问题是如何正确处理可以动态添加/删除的对象的Observable?

1 个答案:

答案 0 :(得分:0)

在深入研究问题的同时,我找到了3种可能的解决方案:

  1. 对于该特定情况,我可以使用控件的Disposed事件:

    var incsFromWidgets = addCounterStream
            .SelectMany(x => Observable.FromEventPattern(x, "Increment")
                .Select(_ => 1)
                .TakeUntil(Observable.FromEventPattern(x, "Disposed").Take(1)))
            .Publish();
    
  2. 实际上它是第一种方法的概括:我可以创建具有发布语义的通用包装类,如:

    class Wrapper<T> : IDisposable {
        public T Value { get; set; }
        public event EventHandler Disposed;        
    }
    
  3. 手动“吊装”溪流:

    // declare all streams
    IObservable<int> aStream = null;
    IObservable<bool> bStream = null;
    IObservable<MyWidget> cStream = null;
    
    // then compose it
    aStream = Observable.From(...).TakeUntil(bStream.Where(...));
    bStream = Observable.From(...).CombineLatest(aStream, ...);  // cyclic dependency
    
  4. 虽然第三种方法可能看起来很糟糕,但它提供了以不同方式组合流的巨大可能性,只需要小心并且不要创建无限递归。

    修改  好吧,实际上第三种方法并不那么简单,即使下面定义了流,也会抛出NullReferenceException。正确的实施方式如下:

            IConnectableObservable<int> a = null;
            IConnectableObservable<char> b = null;
            IConnectableObservable<bool> c = null;            
    
            a = Observable.Defer(() => b.Select(x => (int)x)).Publish();
            b = Observable.Defer(() => Observable.Generate('A', x => x < 255, x => (char)(x + 1), x => x).TakeUntil(c)).Publish();
            c = Observable.Defer(() => a.SkipWhile(x => x < 70).Select(_ => true)).Publish();
    
            a.Subscribe(x => Console.WriteLine($"from a: {x}"));
            b.Subscribe(x => Console.WriteLine($"from b: {x}"));
    
            c.Connect();
            a.Connect();
            b.Connect();
    
            Console.ReadKey(true);