我想知道在向本地函数传递取消令牌时是否需要使用EnumeratorCancellation.我正在考虑将来经常使用这种代码模式:

public static IAsyncEnumerable<string> MyOuterFunctionAsync(
this Client client, 
CancellationToken cancellationToken,
int? limit = null) 
{
    return MyLocalFunction().
        TakeWhile(
            (_, index) => 
                limit is null ||
                index < limit.Value);
 
    async IAsyncEnumerable<string> MyLocalFunction()
    {
       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,当我试图将它添加到外部函数时,它说它不会有任何影响,但如果我try 将它添加到本地函数,ReSharper会很高兴,就像没有.我应该在任何地方使用它吗? 我判断了IL查看器,但我看不出版本之间有任何区别.

MyOuterFunctionAsync能否正常工作?我是否需要将MyLocalFunction签名更改为

async IAsyncEnumerable<string> MyLocalFunction(
    [EnumeratorCancellation] CancellationToken cancellationToken)

推荐答案

您可以考虑采用System.Linq.Async库中使用的模式.例如,Where LINQ运算符:

public static IAsyncEnumerable<TSource> Where<TSource>(
    this IAsyncEnumerable<TSource> source, Func<TSource, 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附加令牌时,由于EnumeratorCancellation属性,该令牌将自动传递给Core实现.

So the general rule is:返回IAsyncEnumerable<T>的方法只有在用async实现的情况下才应该包括CancellationToken参数,在这种情况下,参数也应该用EnumeratorCancellation属性修饰.

理想情况下,返回IAsyncEnumerable<T>的公共API不应该用async实现.这是因为向调用者提供两种不同的选项来传递令牌,无论是直接传递还是通过WithCancellation传递,都会造成混乱,而不会给API增加任何值.有关not要做什么的示例,请参阅ChannelReader<T>.ReadAllAsync API的第implementation部分.


为了直接回答您的问题,MyOuterFunctionAsync将正确识别作为参数传递的CancellationToken,但如果CancellationTokenWithCancellation运算符一起传递,则不会.例如,如果是var sequence = client.MyOuterFunctionAsync(CancellationToken.None).WithCancellation(token);,则token将被忽略.为了获得正确的行为,您必须在MyLocalFunction中添加EnumeratorCancellation属性.

Csharp相关问答推荐

访问C#中的数据库字段时获取数据是收件箱错误-为什么?&有效,如果声明不有效

无法更改或使用C#(WinForms.NET)中的全局变量

Regex在c#中完全匹配

CsWin32如何创建PWSTR的实例,例如GetWindowText

ASP.NET MVC购物车数量更新并从购物车中删除项目

如何定义EFCore中的多个穿透

尽管保证密钥不同,但已添加相同密钥的项(&Q;)

如何将MongoDB序列化程序设置为内部对象属性

如何在Cosmos SDK中控制超时、重试和重试之间的延迟?

EFR32BG22 BLE在SPP模式下与PC(Windows 10)不连接

BFF到具有AAD/Entra ID B2C的内部API(.NET/ASP.NET核心/标识.Web)

如何将%{v_扩展}转换为%{v_扩展}>>

为什么ReadOnlySpan;T&>没有Slice(...)的重载接受Range实例的?

将C#类导入到PowerShell

如何设置WinForms按钮焦点,使其看起来像是被Tab键插入其中?

如何解决System.StackOverflowException:抛出System.StackOverflowException类型的异常.&# 39;生成随机代码时发生异常?

C#USB条形码 scanner 在第二次扫描时未写入行尾字符

ASP.NET核心8:app.UseStaticFiles()管道执行顺序

在Unity C#中按键点击错误的参数

如何更改Datagridview行标题