我需要以某种方式防止在不产生任何值的Observable上调用ToTask时抛出的InvalidOperationException
。
我所看到的是ToTask方法创建了ToTaskObserver
的一个实例,并且它希望至少一个值不引发异常:
private sealed class ToTaskObserver<TResult> : SafeObserver<TResult>
{
//[...]
private bool _hasValue;
private TResult _lastValue;
public ToTaskObserver(TaskCompletionSource<TResult> tcs, CancellationToken ct)
{
//[...]
}
public override void OnNext(TResult value)
{
_hasValue = true;
_lastValue = value;
}
//[...]
public override void OnCompleted()
{
if (_hasValue)
{
_tcs.TrySetResult(_lastValue);
}
else
{
_tcs.TrySetException(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
}
//[...]
}
//[...]
}
我发现的唯一解决方案是模仿扩展方法,并通过合并将虚拟记录插入Observable:
public static Task<Record<TKey, TValue>> ToTask<TKey, TValue>(
this IObservable<Record<TKey, TValue>> source, CancellationToken token)
{
var pseudoObservable = new[] {Record.Create<TKey, TValue>(default, default)}.ToObservable();
return source.Merge(pseudoObservable).ToTask(token);
}
这个问题尤其与ToTask
方法有关。我知道当我使用Subscribe
方法时,我不会遇到这个问题。
有人对此有更好的解决方案吗?我在可观察的场景中将没有任何记录。
答案 0 :(得分:0)
此处有许多选项:
.ToList()
。 default(T)
不是有效值,则可以使用.LastOrDefaultAsync()
,然后检查默认值。default(T)
是有效值,则仍然可以使用.ToList()
,也可以使用Option
样式类:除非我忘记了C#的内置选项(F#有一个),否则Option
类看起来像这样。
public class Optional<T>
{
private readonly bool _hasValue;
private readonly T _value;
public Optional(T t)
{
_value = t;
_hasValue = true;
}
public Optional()
{
_hasValue = false;
_value = default(T);
}
public bool HasValue => _hasValue;
public T Value
{
get
{
if(HasValue)
return _value;
else
throw new InvalidOperationException("No value present");
}
}
}
public static class X {
public static IObservable<Optional<T>> ToOptional<T>(this IObservable<T> source)
{
return source.Publish(_source => _source
.Select(t => new Optional<T>(t))
.LastOrDefaultAsync()
.Select(n => n.Equals(default(Optional<T>)) ? new Optional<T>() : n)
);
}
}
与Optional
相比,我更喜欢List
选项,因为当您知道最大值为一个时,List
看起来很有趣。但是取决于你。这是示例用法:
var fortyTwoList = await Observable.Return(42).ToList();
var noneList = await Observable.Empty<int>().ToList();
var fortyTwo = await Observable.Return(42).ToOptional();
var none = await Observable.Empty<int>().ToOptional();
none.Dump(); //Linqpad
fortyTwo.Dump(); //Linqpad
noneList.Dump(); //Linqpad
fortyTwoList.Dump(); //Linqpad