簡體   English   中英

在IObservable上使用ToTask時防止InvalidOperationException

[英]Prevent InvalidOperationException when using ToTask on IObservable

我需要以某種方式防止在不產生任何值的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方法時,我不會遇到這個問題。

有人對此有更好的解決方案嗎? 我有可觀察的場景將沒有任何記錄的情況。

這里有很多選擇:

  • 如果您期望有多個記錄(並且可能不是因為等待這些記錄效果不好),則可以使用.ToList()
  • 如果期望0或1條記錄,並且default(T)不是有效值,則可以使用.LastOrDefaultAsync() ,然后檢查默認值。
  • 如果期望0或1條記錄,並且default(T)是有效值,則仍可以使用.ToList() ,或者可以使用Option樣式類:

Option類看起來像這樣,除非我忘記了C#的內置選項(F#有一個)。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM