采用以下类,假设Calculate
是一个计算密集型函数。
class Algorithm
{
FinalResultObject Calculate()
{
longPartialCalculation();
//signal to caller that that part is ready of type MidResult1
morePartialCalculation();
//signal more is ready, different type of MidResult2
moreWork();
return finalResult;
}
}
现在假设,只要用户准备就绪,就需要向用户显示中间结果。
我看到的选项是:
使用单独的事件发出信号
使用构造函数注入来注入其方法被调用的处理程序类
使用RX observables
我是RX的新手,但我喜欢我可以轻松地在UI线程上进行事件处理的想法。我想知道这是否过度而且不是预期的,因为它不是真正的整个数据流,而是每个可观察数据的一个结果。另一方面,虽然与事件订阅和取消订阅似乎是如此繁琐。
任何提示?
答案 0 :(得分:3)
解决此问题的Rx方法是定义冷可观察对象,如下所示:
IObservable<Result> Calculate(IScheduler scheduler)
{
return Observable.Create<Result>(observer =>
scheduler.Schedule(() =>
{
observer.OnNext(longPartialCalculation());
observer.OnNext(morePartialCalculation());
observer.OnNext(moreWork());
observer.OnCompleted();
}));
}
// Depending upon your needs, you could use inheritance as follows:
public abstract class Result { ... }
public class MidResult1 : Result { ... }
public class MidResult2 : Result { ... }
public class FinalResultObject : Result { ... }
您还可以定义一个指定默认调度程序的重载,例如ThreadPoolScheduler
如果要引入并发,或CurrentThreadScheduler
如果不引入。
要使用此方法返回的observable,只需使用观察者调用Subscribe
即可。您可以提供OnNext
处理程序来检查每个Result
对象到达时是否有OnCompleted
处理程序来处理完成。如果必须,您还可以提供OnError
处理程序来处理Exception
。
(编辑:请注意,我的示例并未调用OnError
。)
如果要确保所有这些处理程序都在UI线程上执行,并且您已将引入并发的调度程序(例如ThreadPoolScheduler
)传递给Calculate
方法,那么您还可以在基于XAML的平台上应用ObserveOn
运算符(或ObserveOnDispatcher
)来封送到UI线程的所有通知以供观察。
algo.Calculate(ThreadPoolScheduler.Instance)
.ObserveOnDispatcher()
.Subscribe(OnNextResult, OnCompleted);
请注意,Rx的主要优点之一是查询能力;例如,一个简单的过滤器:
algo.Calculate(ThreadPoolScheduler.Instance)
.Where(result => result.HasRequiredState)
.ObserveOnDispatcher()
.Subscribe(result => handle(result.RequiredState));
答案 1 :(得分:2)
您可以使用.Net的Progress<T>
。您可以创建一个实例,传递一个处理程序或注册到它的事件,并在整个长期运行的过程中通过它进行报告:
var progress = new Progress<string>(value => Console.WriteLine(value));
Calculate(progress);
FinalResultObject Calculate(IProgress<string> progress)
{
longPartialCalculation();
progress.Report("MidResult1");
morePartialCalculation();
progress.Report("MidResult2");
moreWork();
return finalResult;
}
在这种情况下,报告会将一个字符串写入控制台,但您当然可以使用任何类型的字符串。
Progress<T>
还会捕获创建时的当前SynchronizationContext
,以便您可以在UI线程中创建它,将其传递给非UI线程而不会出现任何同步问题。