繁体   English   中英

IDisposable 对象的 Observable:如何处理 onNext 和 onComplete 上一个值?

[英]Observable of IDisposable object: How to dispose previous value onNext and onComplete?

我有一个IDisposable值的Observable

IObservable<IDisposable> disposableValues = source.Select(val => MyDisposableObject());

如何编写处理旧值的管道,当:

  1. 发出新值
  2. 源何时完成?

我认为 #1 可以通过Buffer实现,但是如何访问onComplete callbak 上的最后一个值?

disposableValues
    .Buffer(2)
    .Do(buffer => buffer[0].Dispose())
    .Finally(_ => { ??})
    .Subscribe();

也许我做错了,我可以使用CancellationToken或其他东西......

就像我在评论中所说的那样,我认为拥有IObservable<IDisposable>有点奇怪。 在像这样的可观察管道中,很难推断一次性用品的使用寿命。

更有可能的是,您有一个 observable 需要使用一个一次性对象,您想用它来做某事,并且您想确保它在之后被处理。

假设你有这个一次性用品:

public class MyDisposableObject : IDisposable
{
    public void DoSomething()
    {
        Console.WriteLine("DoSomething!");
    }
    
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Console.WriteLine("Dispose!");
            }
            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }
}

它有事情要做,它让我们知道它什么时候处理。

现在进行查询。

var source = new Subject<Unit>();

IObservable<Unit> observable =
    source
        .SelectMany(u =>
            Observable.Using(
                () => new MyDisposableObject(),
                mdo => Observable.Start(() => mdo.DoSomething())));
        
observable.Subscribe();

它使用Observable.Using运算符来创建一次性用品,然后能够启动一个使用一次性用品的 observable。 一旦它完成或者source observable 完成,一切都会被处理掉。

试试这个测试代码:

source.OnNext(Unit.Default);
source.OnNext(Unit.Default);
source.OnNext(Unit.Default);
source.OnCompleted();

这给了我:

DoSomething!
Dispose!
DoSomething!
DoSomething!
Dispose!
Dispose!

一切都得到了很好的处理,并且创建了一个干净的 observable,没有副作用。

使用Buffer()是获取“前一个”对象的正确方法,但您应该使用Buffer(2,1)来获取“滑动窗口”。 然后您以默认值(如null )开始订阅,并让它以默认值(也是null )结束。 目标是获得这样的序列:

[null, obj1]
[obj1, obj2]
[obj2, obj3]
[obj3, null]

您将有四个事件,对于其中三个,您可以调用Dispose() 对于第一个null您可以使用位于实际数据之前的StartWith(null) 对于最后一个null您使用Concat()通过使用Observable.Never()StartWith()添加一个只有一个值Observable.Never() StartWith() 当您的原始 observable 完成时,您将获得一个“假”事件,您可以在其中处置上次使用的对象。 检查以下示例:

public class Program
{
    public class TestObject : IDisposable
    {
        public TestObject(int value) {
            this.Value = value;
        }

        public int Value { get; }

        public void Dispose()
        {
            Console.WriteLine($"Dispose called for {this.Value}");
        }

        public override string ToString()
        {
            return $"TestObject({this.Value})";
        }
    }

    public static void Main()
    {   
        using (MyContext context = new MyContext())
        {
            ISubject<TestObject> source = new Subject<TestObject>();
            IDisposable subscription = source
                .StartWith((TestObject)null)
                .Concat(Observable.Never<TestObject>().StartWith((TestObject)null))
                .Buffer(2,1)
                .Subscribe(it => {
                    Console.WriteLine("Array content: "+String.Join(", ", it));
                    TestObject first = it.First();
                    TestObject last = it.Last();
                    if (first != null) {
                        Console.WriteLine($"Found an old object {first}, dispose it");
                        first.Dispose();
                    } else {
                        Console.WriteLine("There is no previous object");
                    }

                    if (last != null) {
                        Console.WriteLine($"'Current' object is: {last}");
                    } else {
                        Console.WriteLine("No 'current' object");
                    }
                });
            source.OnNext(new TestObject(1));
            source.OnNext(new TestObject(2));
            source.OnNext(new TestObject(3));
            source.OnCompleted();
            Console.WriteLine("Finished");
        }
    }
}

这将生成以下调试输出:

Array content: , TestObject(1)
There is no previous object
'Current' object is: TestObject(1)
Array content: TestObject(1), TestObject(2)
Found an old object TestObject(1), dispose it
Dispose called for 1
'Current' object is: TestObject(2)
Array content: TestObject(2), TestObject(3)
Found an old object TestObject(2), dispose it
Dispose called for 2
'Current' object is: TestObject(3)
Array content: TestObject(3), 
Found an old object TestObject(3), dispose it
Dispose called for 3
No 'current' object
Finished

您可能想要的是与Do运算符类似的东西,但用于前一个元素。 下面是自定义DoOnPrevious运算符的实现:

/// <summary>Invokes an action for the previous element in the observable
/// sequence. The action for the last element is invoked when the observable
/// sequence completes.</summary>
private static IObservable<T> DoOnPrevious<T>(this IObservable<T> source,
    Action<T> onPrevious)
{
    return source
        .Select(x => (Item: x, HasValue: true))
        .Append((default, false))
        .Scan((previous, current) =>
        {
            if (previous.HasValue) onPrevious(previous.Item);
            return current;
        })
        .Where(entry => entry.HasValue)
        .Select(entry => entry.Item);
}

用法示例:

disposableValues
    .DoOnPrevious(x => x.Dispose())
    .Subscribe();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM