Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# IDisposable对象的可观察性:如何处理上一个值onNext和onComplete?_C#_System.reactive_Rx.net - Fatal编程技术网

C# IDisposable对象的可观察性:如何处理上一个值onNext和onComplete?

C# IDisposable对象的可观察性:如何处理上一个值onNext和onComplete?,c#,system.reactive,rx.net,C#,System.reactive,Rx.net,我有一个IDisposable值的Observable IObservable<IDisposable> disposableValues = source.Select(val => MyDisposableObject()); 也许我做错了,我可以使用CancellationToken或其他什么…使用Buffer()是获取“previous”对象的正确方法,但您应该使用Buffer(2,1)来获取“滑动窗口”。然后,以默认值(如null)开始订阅,并以默认值(也是null

我有一个
IDisposable
值的
Observable

IObservable<IDisposable> disposableValues = source.Select(val => MyDisposableObject());
也许我做错了,我可以使用
CancellationToken
或其他什么…

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

[null, obj1]
[obj1, obj2]
[obj2, obj3]
[obj3, null]
您将有四个事件,其中三个事件可以调用
Dispose()
。 对于第一个
null
可以使用实际数据之前的
StartWith(null)
。对于最后一个
null
您可以使用
Concat()
通过使用
Observable.Never()
StartWith()
来添加一个。当您的原始可观察对象完成时,您将得到一个“假”事件,您可以在其中处置您最后使用的对象。检查以下示例:

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");
        }
    }
}

您可能想要的是与操作符类似的东西,但对于前面的元素。下面是自定义
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);
}

正如我在评论中所说的,我认为有一个
IObservable
有点奇怪。在这样一个可观察的管道中,很难推断一次性塑料的寿命

更可能的情况是,您有一个可观察对象,需要使用一个您想要处理的一次性对象,并且您希望确保它在处理后得到处理

让我们假设你有这个一次性的:

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();
这给了我:

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

一切都被很好地处理,并且创建了一个干净的可观察对象,没有任何副作用。

实际上我不需要从null开始,因为我不能处理null。我可以使用Append(null)代替concat,对吗?这使它更容易理解readable@Liero如果不以
null
开头,则不会看到
[null,obj1]
事件/数据。这意味着您可能无法正确地对第一个对象作出反应,而是从源中的第二个对象开始。我不知道存在
Append()
,但它看起来也在做同样的事情(但要简单得多)。为什么我需要
[null,obj1]
,当存在
if(first!=null)
时?@Liero如果没有
[null,obj1]
事件,您可能会“错过”第一个条目,因为序列将以
[obj1,obj2]开始
您可以只看第二个参数。这假设您对来自原始源的当前对象有一些实际的有效负载。如果此特定订阅仅用于处置一次性物品,而不使用“当前”条目执行任何操作,则可以跳过第一个
[null,obj1]
条目。看起来您确实做错了什么。有一个
IObservable
是很奇怪的。你能不能给我们一点关于这个可观察的东西是什么意思的上下文?显然,您不仅仅是在创建一次性物品并在不以某种方式使用它们的情况下对其进行处理。相关:
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();
source.OnNext(Unit.Default);
source.OnNext(Unit.Default);
source.OnNext(Unit.Default);
source.OnCompleted();
DoSomething!
Dispose!
DoSomething!
DoSomething!
Dispose!
Dispose!