James James &理查德提出了一些很好的观点,但我认为他们没有给你最好的方法来解决你的问题
James建议使用.Catch(Observable.Never<Unit>())
。当他说“将...允许流继续”时,他错了,因为一旦你遇到异常,流必须结束 - 这是理查德在提到观察者和观察者之间的合同时指出的。
此外,以这种方式使用Never
将导致您的观测值永远不会完成。
简而言之,.Catch(Observable.Empty<Unit>())
是将序列从一个以错误结尾的序列更改为以完成结束的序列的正确方法。
您已经达到了使用SelectMany
来处理源集合的每个值的正确想法,以便您可以捕获每个异常,但是您留下了一些问题。
您正在使用任务(TPL)只是将函数调用转换为可观察值。这会强制您的observable使用任务池线程,这意味着SelectMany
语句可能会以非确定性顺序生成值。
此外,您还隐藏实际调用来处理数据,使重构和维护变得更困难。
我认为你最好创建一个允许跳过异常的扩展方法。那就是:
public static IObservable<R> SelectAndSkipOnException<T, R>(
this IObservable<T> source, Func<T, R> selector)
{
return
source
.Select(t =>
Observable.Start(() => selector(t)).Catch(Observable.Empty<R>()))
.Merge();
}
用这种方法你现在可以简单地这样做:
var result =
collection.ToObservable()
.SelectAndSkipOnException(t =>
{
var a = DoA(t);
var b = DoB(a);
var c = DoC(b);
return c;
});
这段代码要简单得多,但它隐藏了异常(S)。如果你想继续让异常继续下去,那么你需要做一些额外的功能。在Materialize
扩展方法中增加一些重载可以保持错误。
public static IObservable<Notification<R>> Materialize<T, R>(
this IObservable<T> source, Func<T, R> selector)
{
return source.Select(t => Notification.CreateOnNext(t)).Materialize(selector);
}
public static IObservable<Notification<R>> Materialize<T, R>(
this IObservable<Notification<T>> source, Func<T, R> selector)
{
Func<Notification<T>, Notification<R>> f = nt =>
{
if (nt.Kind == NotificationKind.OnNext)
{
try
{
return Notification.CreateOnNext<R>(selector(nt.Value));
}
catch (Exception ex)
{
ex.Data["Value"] = nt.Value;
ex.Data["Selector"] = selector;
return Notification.CreateOnError<R>(ex);
}
}
else
{
if (nt.Kind == NotificationKind.OnError)
{
return Notification.CreateOnError<R>(nt.Exception);
}
else
{
return Notification.CreateOnCompleted<R>();
}
}
};
return source.Select(nt => f(nt));
}
这些方法允许你这样写:
var result =
collection
.ToObservable()
.Materialize(t =>
{
var a = DoA(t);
var b = DoB(a);
var c = DoC(b);
return c;
})
.Do(nt =>
{
if (nt.Kind == NotificationKind.OnError)
{
/* Process the error in `nt.Exception` */
}
})
.Where(nt => nt.Kind != NotificationKind.OnError)
.Dematerialize();
你甚至可以链接这些Materialize
方法和使用ex.Data["Value"]
& ex.Data["Selector"]
得到抛出错误出来的价值和选择功能。
我希望这会有所帮助。
我已更新该帖子以包含我正在尝试完成的场景 – 2011-05-20 21:08:25