[英]Observable of IDisposable object: How to dispose previous value onNext and onComplete?
我有一个IDisposable
值的Observable
IObservable<IDisposable> disposableValues = source.Select(val => MyDisposableObject());
如何编写处理旧值的管道,当:
我认为 #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.