我想知道在将取消令牌传递给我的内部函数时是否需要使用
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 查看器,但我没有看到版本之间有任何差异。
您可以考虑采用 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 的实现。