Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.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# 如果在下一个事件到达之前未完成选择,如何取消接收中的选择_C#_System.reactive - Fatal编程技术网

C# 如果在下一个事件到达之前未完成选择,如何取消接收中的选择

C# 如果在下一个事件到达之前未完成选择,如何取消接收中的选择,c#,system.reactive,C#,System.reactive,我有以下设置 IObservable<Data> source = ...; source .Select(data=>VeryExpensiveOperation(data)) .Subscribe(data=>Console.WriteLine(data)); 因此,每次调用lambda时,都会使用cancelToken进行调用,cancelToken可以 用于管理取消任务。然而,现在我们正在混合任务、取消令牌和接收。 不太确定如何将其全部装配在一

我有以下设置

IObservable<Data> source = ...;

source
    .Select(data=>VeryExpensiveOperation(data))
    .Subscribe(data=>Console.WriteLine(data));
因此,每次调用lambda时,都会使用cancelToken进行调用,cancelToken可以 用于管理取消
任务
。然而,现在我们正在混合任务、取消令牌和接收。 不太确定如何将其全部装配在一起。任何建议

计算出如何使用XUnit测试操作员的奖励积分:)

第一次尝试

    public static IObservable<U> SelectWithCancelation<T, U>( this IObservable<T> This, Func<CancellationToken, T, Task<U>> fn )
    {
        CancellationTokenSource tokenSource = new CancellationTokenSource();

        return This
            .ObserveOn(Scheduler.Default)
            .Select(v=>{
                tokenSource.Cancel();
                tokenSource=new CancellationTokenSource();
                return new {tokenSource.Token, v};
            })
            .SelectMany(o=>Observable.FromAsync(()=>fn(o.Token, o.v)));
    }
public static IObservable selectwithcancellation(this IObservable this,Func fn)
{
CancellationTokenSource tokenSource=新的CancellationTokenSource();
还这个
.ObserveOn(Scheduler.Default)
.选择(v=>{
tokenSource.Cancel();
tokenSource=新的CancellationTokenSource();
返回新的{tokenSource.Token,v};
})
.SelectMany(o=>Observable.fromsync(()=>fn(o.Token,o.v));
}

还没有测试。我希望未完成的任务会生成一个IObservable,该IObservable在完成时不会触发任何
OnNext
事件。

您必须将
非常昂贵的操作
建模为可取消的异步操作。无论是
任务
还是
IObservable
。我假设这是一个带有
取消令牌的任务:

Task<TResult> VeryExpensiveOperationAsync<TSource, TResult>(TSource item, CancellationToken token);
Select
只创建了一个延迟的可观察对象,当订阅时,它将创建一个令牌并启动操作。如果在操作完成之前取消订阅了observable,则令牌将被取消

开关
订阅来自
选择
的每个新观察对象,取消订阅之前订阅的观察对象

这有你想要的效果

另外,这很容易测试。只需提供一个模拟源和一个使用单元测试提供的
taskcompletionsource
的模拟
VeryExpensiveOperation
,这样单元测试就可以精确控制何时生成新的源项以及何时完成任务。大概是这样的:

void SomeTest()
{
    // create a test source where the values are how long
    // the mock operation should wait to do its work.
    var source = _testScheduler.CreateColdObservable<int>(...);

    // records the actions (whether they completed or canceled)
    List<bool> mockActionsCompleted = new List<bool>();
    var resultStream = source.SelectWithCancellation((token, delay) =>
    {
        var tcs = new TaskCompletionSource<string>();
        var tokenRegistration = new SingleAssignmentDisposable();

        // schedule an action to complete the task
        var d = _testScheduler.ScheduleRelative(delay, () =>
        {
           mockActionsCompleted.Add(true);
           tcs.SetResult("done " + delay);
           // stop listening to the token
           tokenRegistration.Dispose();
        });

        // listen to the token and cancel the task if the token signals
        tokenRegistration.Disposable = token.Register(() =>
        {
           mockActionsCompleted.Add(false);
           tcs.TrySetCancelled();
           // cancel the scheduled task
           d.Dispose();
        });

        return tcs.Task;
    });

    // subscribe to resultStream
    // start the scheduler
    // assert the mockActionsCompleted has the correct sequence
    // assert the results observed were what you expected.
}
void SomeTest()
{
//创建一个测试源,其中的值是多长
//模拟操作应等待完成其工作。
var source=_testScheduler.CreateColdObservable(…);
//记录操作(无论是已完成还是已取消)
List mockActionsCompleted=新建列表();
var resultStream=source.SelectWithCancellation((令牌,延迟)=>
{
var tcs=new TaskCompletionSource();
var tokenRegistration=new SingleAssignmentDisposable();
//安排一项操作以完成任务
var d=\u testScheduler.ScheduleRelative(延迟,()=>
{
mockActionsCompleted.Add(true);
tcs.设置结果(“完成”+延迟);
//停止收听令牌
tokenRegistration.Dispose();
});
//侦听令牌并在令牌发出信号时取消任务
tokenRegistration.Dispossible=token.Register(()=>
{
mockActionsCompleted.Add(false);
tcs.TrySetCancelled();
//取消计划的任务
d、 处置();
});
返回tcs.Task;
});
//订阅resultStream
//启动调度程序
//断言mockActionsCompleted具有正确的序列
//断言观察到的结果是您所期望的。
}

由于动态调度的新操作,使用
testScheduler.Start()
可能会遇到问题。使用
testScheduler.AdvanceBy(1)
进行while循环可能会更好

为什么不使用油门呢

油门停止事件流,直到在指定的时间段内不再产生事件。例如,如果将文本框的TextChanged事件限制为.5秒,则在用户停止键入0.5秒之前,不会传递任何事件。这在搜索框中很有用,在搜索框中,您不希望在每次击键后开始新的搜索,而是希望等待用户暂停

SearchTextChangedObservable=Observable.FromEventPattern(this.textBox,“TextChanged”);
_currentSubscription=SearchTextChangedObservable.Throttle(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher

我不太明白
令牌在哪里被取消。当Switch取消订阅上一个内部可观察对象时,是否自动完成此操作?
DeferAsync
在可观察对象被订阅时创建令牌。然后,如果在任务生成真正的可观察对象之前未订阅该可观察对象,它将取消该标记(让您知道您不需要继续工作来生成真正的可观察对象)
Switch
不断订阅到达的新observable,并取消订阅上一个observable,这会触发上一个observable的取消。我无法编写一个非常昂贵的模拟操作。问题是我必须使用Task.Run来创建它,这会将它放在TestScheduler上下文之外的另一个线程上。然后我没有得到正确的结果。我添加了一个我所想的例子。你用了错误的方法。事件不会来得太快。不需要节流。处理过程非常昂贵,如果有新的数据可用,应该取消。实际上,我的意思是在开始昂贵的处理之前进行节流,这样我们只有在知道它有时间完成时才能运行它。然而,我现在可以看到,与Brandon的实现相比,这只是对它需要完成多长时间的一个初步估计。
source
    .Select(item => Observable.DeferAsync(async token =>
    {
        // do not yield the observable until after the operation is completed
        // (ie do not just do VeryExpensiveOperation(...).ToObservable())
        // because DeferAsync() will dispose of the token source as soon
        // as you provide the observable (instead of when the observable completes)
        var result = await VeryExpensiveOperationAsync(item, token);
        return Observable.Return(result);
    })
    .Switch();
void SomeTest()
{
    // create a test source where the values are how long
    // the mock operation should wait to do its work.
    var source = _testScheduler.CreateColdObservable<int>(...);

    // records the actions (whether they completed or canceled)
    List<bool> mockActionsCompleted = new List<bool>();
    var resultStream = source.SelectWithCancellation((token, delay) =>
    {
        var tcs = new TaskCompletionSource<string>();
        var tokenRegistration = new SingleAssignmentDisposable();

        // schedule an action to complete the task
        var d = _testScheduler.ScheduleRelative(delay, () =>
        {
           mockActionsCompleted.Add(true);
           tcs.SetResult("done " + delay);
           // stop listening to the token
           tokenRegistration.Dispose();
        });

        // listen to the token and cancel the task if the token signals
        tokenRegistration.Disposable = token.Register(() =>
        {
           mockActionsCompleted.Add(false);
           tcs.TrySetCancelled();
           // cancel the scheduled task
           d.Dispose();
        });

        return tcs.Task;
    });

    // subscribe to resultStream
    // start the scheduler
    // assert the mockActionsCompleted has the correct sequence
    // assert the results observed were what you expected.
}
SearchTextChangedObservable = Observable.FromEventPattern<TextChangedEventArgs>(this.textBox, "TextChanged");
_currentSubscription = SearchTextChangedObservable.Throttle(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher