如何从中创建流?

时间:2017-05-14 12:31:18

标签: c# system.reactive

我试图接触Reactive Extensions,但到目前为止我还没有得到它。所以,我想我会尝试一些练习来开始。 " Hello World!"来自rxkoans.codeplex.com的类型示例非常简单,所以这让我很开心。但是,当我想做一些更复杂的事情时,感觉就像我还没有得到这个范例......

我发现这个问题最接近我想要尝试的问题:How to do I show progress when using Reactive Extensions in C#

现在,假设我想要以某种昂贵的方式处理未知项目的进度报告。此外,我不想在我的观察中计算中的最后一项,但我想要所有中间结果。为了一开始保持简单,我只是采用了一个现有的例子,并首先得到了中间结果并运行:

    [TestMethod]
    public void TODO2()
    {
        string result = "";
        Calculate2().Subscribe(r => result += r.Result);

        Assert.AreEqual("do1do2do3do1", result);
        // PASSES
    }

    public IObservable<ResultWithProgress<string>> Calculate2()
    {
        return Observable.Create<ResultWithProgress<string>>(obs =>
        {
            Action<int, string, string> report = (pv, r, pt) =>
            {
                obs.OnNext(new ResultWithProgress<string>()
                {
                    Progress = pv,
                    Result = r,
                    ProgressText = pt,
                });
            };

            var query =
                from result1 in Observable.Start(() => Do1()) // Just returns string "do1"
                    .Do(x => report(25, x, "Completed Task1"))
                from result2 in Observable.Start(() => Do2()) // "do2"
                    .Do(x => report(50, x, "Completed Task2"))
                from result3 in Observable.Start(() => Do3()) // "do3"
                    .Do(x => report(75, x, "Completed Task3"))
                from result4 in Observable.Start(() => Do1()) // "do1" again
                select new ResultWithProgress<string>()
                {
                    Progress = 100,
                    Result = result4,
                    ProgressText = "Done!",
                };

            return query.Subscribe(obs);
        });
    }

然后,我想我应该解决这个问题,即我没有固定数量的元素/计算。我现在开始使用4个元素的数组,但它可以是任何数字。我事先不知道,这将是一个懒惰的序列。

    [TestMethod]
    public void TODO3()
    {
        string result = "";
        Calculate3().Subscribe(r => result += r.Result);

        Assert.AreEqual("do1do2do3do1", result);
        // Doesn't compile yet, see below...
    }

    public IObservable<ResultWithProgress<string>> Calculate3()
    {
        return Observable.Create<ResultWithProgress<string>>(obs =>
        {
            Action<int, string, string> report = (pv, r, pt) =>
            {
                obs.OnNext(new ResultWithProgress<string>()
                {
                    Progress = pv,
                    Result = r,
                    ProgressText = pt,
                });
            };

            int[] bla = new int[] { 1, 2, 3, 4 };
            foreach(var b in bla)
            {
                Observable.Start(() => "BLAH" /* expensive operation here in the future */)
                    .Do(x => report(25 * b, x, "Completed Task1"));
            }

            // Completed!
            obs.OnCompleted();

            // I want an Observable that emits "BLAH" four times 
            // and then signals that it is completed
            // What do I return here? 
            return ?????; 
        });
    }

我确定这只是我对Rx缺乏了解,我刚开始使用它。当你没有固定数量的操作时,是否有人可以帮助我了解我应该做些什么来获得结果?

谢天谢地。

1 个答案:

答案 0 :(得分:1)

在第一个示例中,他们构建了一个查询,然后返回query.Subscribe(obs)。这是正确的做法。

在您的代码中,您明确地将值传递给观察者。您可以这样做,但不建议这样做,因为它可能会创建阻止或行为不正确的查询。当某些事情在Rx中表现不正确时,很难解决。

正确的做法是:

public IObservable<ResultWithProgress<string>> Calculate3()
{
    return Observable.Create<ResultWithProgress<string>>(obs =>
    {
        var query =
            from b in new int[] { 1, 2, 3, 4 }.ToObservable()
            from x in Observable.Start(() => "BLAH" /* expensive operation here in the future */)
            select new ResultWithProgress<string>()
            {
                Progress = 25 * b,
                Result = x,
                ProgressText = "Completed " + b,
            };

        return query.Subscribe(obs);
    });
}

但现在这只是一点点矫枉过正,所以这就足够了:

public IObservable<ResultWithProgress<string>> Calculate3()
{
    return
        from b in new int[] { 1, 2, 3, 4 }.ToObservable()
        from x in Observable.Start(() => "BLAH" /* expensive operation here in the future */)
        select new ResultWithProgress<string>()
        {
            Progress = 25 * b,
            Result = x,
            ProgressText = "Completed " + b,
        };
}

简单是最好的。

唯一的另一个评论是你的专栏Observable.Start(() => "BLAH" /* expensive operation here in the future */).Do(x => report(25 * b, x, "Completed Task1"));什么都不做。 Rx就像普通的可枚举一样,它被懒惰地评估,所以在订阅之前没有任何事情发生。