'等待'是一个可观察的

时间:2012-07-30 07:56:05

标签: c# task-parallel-library system.reactive

我遇到的情况是我正在处理的任务列表(启用驱动器,更改位置,等待停止,禁用)。

'等待'监视IObservable<Status>,我想等待(所以我可以通过ContinueWith和其他任务)。

我开始使用以下任务中对订阅者的OnNext处理,但这只是丑陋。我现在提出的是这种扩展方法:

public static Task<T> WaitFor<T>(this IObservable<T> source, Func<T, bool> pred)
{
    var tcs = new TaskCompletionSource<T>();
    source
        .Where(pred)
        .DistinctUntilChanged()
        .Take(1)  //OnCompletes the observable, subscription will self-dispose
        .Subscribe(val => tcs.TrySetResult(val),
                    ex => tcs.TrySetException(ex),
                    () => tcs.TrySetCanceled());

    return tcs.Task;
}

更新,svick建议处理OnCompletedOnError

问题:

  • 这是好事,坏事还是丑陋?
  • 我是否错过了可以这样做的现有分机?
  • WhereDistinctUntilChanged的顺序是否正确? (我认为他们是)

2 个答案:

答案 0 :(得分:11)

至少我会将此扩展方法更改为:

public static Task<T> WaitFor<T>(this IObservable<T> source, Func<T, bool> pred)
{
    return
        source
            .Where(pred)
            .DistinctUntilChanged()
            .Take(1)
            .ToTask();
}

使用.ToTask()比引入TaskCompletionSource要好得多。您需要引用System.Reactive.Threading.Tasks命名空间才能获得.ToTask()扩展方法。

此外,DistinctUntilChanged在此代码中是多余的。您只获得一个值,因此默认情况下它必须是不同的。

现在,我的下一个建议可能有点争议。这个扩展是一个坏主意,因为它隐藏了正在发生的事情的真实语义。

如果我有这两个代码片段:

var t = xs.WaitFor(x => x > 10);

或者:

var t = xs.Where(x => x > 10).Take(1).ToTask();

我通常更喜欢第二个snippit,因为它清楚地告诉我发生了什么 - 我不需要记住WaitFor的语义。

除非您将WaitFor的名称更具描述性 - 也许TakeOneAsTaskWhere - 否则您将清楚地使用运算符,而不是使用运算符并使代码更难管理。 / p>

以下内容是否更容易记住语义?

var t = xs.TakeOneAsTaskWhere(x => x > 10);

对我来说,最重要的是Rx运算符应该是组合的,而不是封装的,但是如果你要封装它们,那么它们的含义必须清楚。

我希望这会有所帮助。

答案 1 :(得分:3)

不是100%肯定这一点,但是从阅读Rx 2.0 beta博客文章,我认为如果你可以使用async / await,你可以“返回等待source.FirstAsync(pred)”或没有异步,“返回source.FirstAsync(预解码值).ToTask()“

http://blogs.msdn.com/b/rxteam/archive/2012/03/12/reactive-extensions-v2-0-beta-available-now.aspx

sshot of linqpad using rx 2.0 and firstasync