嵌套 IAsyncEnumerable 函数需要 EnumeratorCancellation 属性?

问题描述 投票:0回答:1

我想知道在将取消令牌传递给我的内部函数时是否需要使用

EnumeratorCancellation
。我正在考虑将来经常使用这种代码模式:

public static IAsyncEnumerable<string> MyOuterFunctionAsync(
this Client client, 
CancellationToken cancellationToken,
int? limit = null) 
{
    return limit is null
        ? MyInnerFunction()
        : MyInnerFunction().Take(limit.Value);
 
    async IAsyncEnumerable<string> MyInnerFunction()
    {
       var request = CreateRequest();

       do 
       {
            var page = await request.GetAsync(cancellationToken);
            foreach (var item in page) 
            {
                yield return item;
            }
            request = GetNextPageRequest();
       }
       while (request is not null)
    }
}

Resharper 没有提到需要

EnumeratorCancellation
,当我尝试将其添加到外部函数时,它说它将不会有任何效果,但如果我尝试将其添加到内部函数,Resharper 会保持满意,就像没有一样。我应该在任何地方使用它吗? 我检查了 IL 查看器,但我没有看到版本之间有任何差异。

c# asynchronous async-await cancellation-token iasyncenumerable
1个回答
0
投票

您可以考虑采用 System.Linq.Async 库中使用的模式。例如

Where
LINQ 运算符:

public static IAsyncEnumerable<TSource> Where<TSource>(
    this IAsyncEnumerable<TSource> source, Func<TSource, int, bool> predicate)
{
    if (source == null)
        throw Error.ArgumentNull(nameof(source));
    if (predicate == null)
        throw Error.ArgumentNull(nameof(predicate));

    return Core(source, predicate);

    static async IAsyncEnumerable<TSource> Core(IAsyncEnumerable<TSource> source,
        Func<TSource, bool> predicate,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)
    {
        await foreach (var element in source.WithCancellation(cancellationToken)
            .ConfigureAwait(false))
        {
            if (predicate(element))
            {
                yield return element;
            }
        }
    }
}

如您所见,

Where
运算符的公共签名不包含
CancellationToken
参数。调用者始终可以使用
WithCancellation
运算符将
CancellationToken
附加到异步序列,因此包含参数是多余的。

另一方面,运算符的本地

Core
实现确实包含
CancellationToken
参数,该参数也用
EnumeratorCancellation
属性修饰。当调用者使用
Where
并附加带有
WithCancellation
的令牌时,由于
Core
属性,该令牌会自动传递到
EnumeratorCancellation
实现。

所以一般规则是: 返回

IAsyncEnumerable<T>
的方法仅当使用
CancellationToken
实现时才应包含
async
参数, 在这种情况下,参数还应该用
EnumeratorCancellation
属性修饰。

理想情况下,返回

IAsyncEnumerable<T>
的公共 API 不应使用
async
来实现。这是因为向调用者提供两种不同的选项来传递令牌(直接或通过
WithCancellation
)会造成混乱,而不会给 API 增加任何价值。作为做什么的示例,请参阅 ChannelReader<T>.ReadAllAsync
 API 的
实现

© www.soinside.com 2019 - 2024. All rights reserved.