使用异步调用替代 bool TryGetX(out example)

Alternative to bool TryGetX(out example) with async calls

我真的很喜欢 bool TryGetX(out example) 的方式。问题是我想使用 GetKlinesAsync 并且它不允许我在异步方法中使用 out/ref 。你能向我推荐一种将它与异步 GetKlinesAsync 一起使用的方法吗?

我在想:

public (bool Success, IList<IOhlv> Candles) GetCandlesAsync(...)

意见?

片段

public class Client
{
    // Works fine
    public bool TryGetCandles(
        string symbol,
        KlineInterval timeInterval,
        out IList<IOhlcv> candles,
        DateTime? startTime = null,
        DateTime? endTime = null,
        int? limit = null)
    {
        var result = new BinanceClient().Spot.Market.GetKlines(symbol, timeInterval, startTime, endTime, limit);
        candles = result.Data?.Select(x => x.ToCandle()).ToList();
        return result.Success;
    }
    
    // Compile-time error
    public async System.Threading.Tasks.Task<bool> TryGetCandlesAsync(
       string symbol,
       KlineInterval timeInterval,
       out IList<IOhlcv> candles, // error CS1988: Async methods cannot have ref, in or out parameters
       DateTime? startTime = null,
       DateTime? endTime = null,
       int? limit = null)
    {
        var result = await new BinanceClient().Spot.Market.GetKlinesAsync(symbol, timeInterval, startTime, endTime, limit).ConfigureAwait(false);
        candles = result.Data?.Select(x => x.ToCandle()).ToList();
        return result.Success;
    }
}

当您使用 async 关键字时,编译器会生成一个 class / 状态机来保存所有局部变量并管理 async 和 await 模式. CLR 无法将 out/ref 参数的地址安全地存储在 class 的字段中(就像迭代器方法和其他生成的 classes 一样)。

有几个选项,但常见的方法是 return ValueTuple

public async Task<(bool success, IList<IOhlcv> candles)> TryGetCandlesAsync(...)
{
    return (result.Success, candles)
}


...

var (success, candles) = await TryGetCandlesAsync();
if (success)
{
    // do something candles;
}

如果你真的想要这一行,你可以使用模式匹配..虽然我个人会坚持使用单独的语句,因为在除了最简单的情况下,这会很快变得丑陋,并且有可能引入微妙的如果你偏离丢弃的问题

if (await TryGetCandlesAsync() is (bool success, _) result && success)
     Console.WriteLine(result.candles);

// or

if (await TryGetCandlesAsync() is (true, _) result)
     Console.WriteLine(result.candles);