2

我目前正在为一个应用程序构建一个向导系统,我们正在使用 ReactiveUI,因此是 Rx。

向导中的每一步都实现IWizardStep<T>T向导最终生成的数据类型。

每个步骤都具有显示用户可以移动到下一个步骤的能力,以便基于数据输入启用分支。

可以认为该步骤具有与以下类似的结构:

public interface IWizardStep<T>
{
    IObservable<IStepChoice<T>> NextStepChoice {get;}
}

只需IStepChoice<T>

public interface IStepChoice<T>
{
    IWizardStep<T> Step {get;}
    string Reason {get;}
}

为了计算从开始到结束的当前路径,以显示给用户,我需要能够从开始步骤开始,并NextStepChoice递归地沿着链走,直到它达到空值(这是NextStepChoice可观察的有效行为发出 null 以指示向导结束)。

我已经看过了,Observable.Scan但我一生都无法弄清楚如何以递归方式使其正常工作。

我还看了看Observable.Generate哪个看起来很有希望,因为这是一个经典的展开式问题;唯一的问题是Generate需要一个函数来确定何时中断循环,但我需要评估内部 observable 来解决这个问题。

Observable.Generate(
    new WizardStepChoice<T>(start, null),
    choice => choice != null,
    choice => choice.ChosenStep.NextStepChoice,
    choice => choice);

这将是理想的,并产生我想要的输出,但NextStepChoice那里的选择器显然无法编译,因为它是 anIObservable<IWizardStepChoice<T>>而不是IWizardStepChoice<T>.

我看过使用AggregateScan但是由于这些是更多折叠驱动的操作,而且我只有起始元素,这是我正在寻找的展开 ala Generate,但我需要它能够评估嵌套的 observable。

Observable.Create也许是我可以利用的东西?我已经尝试过了,并想出了:

Path = CurrentStep.Select(_ => Observable.Create<IWizardStep<T>>(async observer =>
 {
     IWizardStepChoice<T> next = new WizardStepChoice<T>(start, null);
     observer.OnNext(next.ChosenStep);
     while (next != null)
     {
         next = await next.ChosenStep.NextStepChoice;
         observer.OnNext(next.ChosenStep);
     }
     observer.OnCompleted();
     return Disposable.Empty;
 }).Aggregate(new List<IWizardStep<T>>(),
 (l, s) =>
 {
     l.Add(s);
     return l;
 })).Switch().Publish().RefCount();

哪个有我想要的所有正确签名IWizardStep<T>->IReadOnlyList<IWizardStep<T>>,所以乍一看它看起来不错,但它不起作用;它触发了,我可以通过,但是一旦它到达等待它就会挂起并且不会回来。

我感觉我很接近了,这是一个日程安排问题,所以我的问题是:

  1. 解决这个问题的最佳方法是什么,我接近了吗?
  2. 如果这是正确的,为什么等待会出现问题,我该如何解决?

更新

经过一些修补后,我注意到 await 可能挂起,因为该 observable 尚未(并且不会)发出一个值(duh),我现在通过初始化每个步骤的值来解决这个问题向导的开始。

我什至通过添加一个属性来检查这一点IWizardStep<T>-IWizardStepChoice<T> LatestStepChoice {get;}这只是与:

NextStepChoice.Subscribe(c => _latestStepChoice = c);

这是在 step 类本身上完成的,我可以确认它工作得很好。

然而等待仍然挂起,所以我尝试了:

  1. 让它Replay(1)等待调用.Subscribe()将获得价值 - 这不起作用
  2. Repeat()即使订阅了某些东西,它也会看到新的价值 - 这只是让整个事情挂起。

显然我在这里遗漏了一些东西,我想要它,以便当 await 查询 observable 时,它​​将被赋予看到的最新值,这是我认为Replay(1)会实现的;我也尝试过,但由于这种行为PublishLast(),未来的更新不会得到尊重。AsyncSubject<T>

现在我已经切换到使用 self-subscribed 属性,但这并不理想,如果我能提供帮助,我宁愿不必中断查询 observables,感觉“hacky”。

4

1 回答 1

2

递归遍历可以将可观察对象树转换为单个可观察对象:

    static IObservable<IWizardStep<T>> Walk<T>(IWizardStep<T> step)
    {
        if (step?.NextStepChoice == null)
            return Observable.Return(step);

        return step.NextStepChoice.SelectMany(choice => Walk(choice.Step)).StartWith(step);
    }

用法:

var steps = await Walk(step).ToArray();
于 2017-02-26T07:19:44.653 回答