通过


解决使用 await 运算符的异步方法中的错误和警告

本文介绍以下编译器错误:

  • CS1983由于这是异步方法,因此返回表达式的类型必须为“”T而不是“”。Task<T>
  • CS1985无法在 catch 子句中使用 await。
  • CS1986await”要求类型具有合适的“GetAwaiter”方法。
  • CS1989不能将异步 lambda 表达式转换为表达式树。
  • CS1991“Type”无法实现“event”,因为它是 Windows 运行时事件,“event”是常规的 .NET 事件。
  • CS1992仅当包含在用“async”修饰符标记的方法或 lambda 表达式内时,才能使用“'await”运算符。
  • CS1994async修饰符只能在具有主体的方法中使用。
  • CS1995await” 运算符只能在初始 “from” 子句的第一个集合表达式或在 “join” 子句的集合表达式中使用。
  • CS1996不能在 lock 语句体中使用 await。
  • CS1997由于函数是返回值的异步方法,因此返回关键字不得后跟对象表达式。
  • CS1998此异步方法缺少“”await运算符,并且将同步运行。请考虑使用“”await运算符等待非阻塞 API 调用,或“”await Task.Run(...)在后台线程上执行 CPU 绑定工作。
  • CS4001无法等待表达式。
  • CS4003await不能用作异步方法或 lambda 表达式中的标识符。
  • CS4005异步方法不能具有指针类型参数。
  • CS4006异步方法的参数列表中不允许__arglist。
  • CS4007无法保留类型实例跨越“await”或“yield”边界。
  • CS4008无法等待“void”。
  • CS4009返回入口点的 void 或 int 不能是异步的。
  • CS4010无法将异步表达式转换为委托类型。异步表达式可能返回 void、Task 或 Task<T>,而不能转换为类型。
  • CS4011await要求返回类型为”{1}。GetAwaiter()具有合适的“IsCompleted”、“OnCompleted”和“GetResult”成员,并实现“INotifyCompletion”或“ICriticalNotifyCompletion”。
  • CS4012类型参数不能在异步方法或异步 lambda 表达式中声明。
  • CS4014由于此调用未等待,因此在调用完成之前,当前方法的执行将继续执行。请考虑将 await 运算符应用于调用的结果。
  • CS4015“MethodImplOptions.Synchronized”不能应用于异步方法。
  • CS4016由于这是异步方法,因此返回表达式的类型必须是类似于 Task 的类型,而不是声明的类型。
  • CS4027表达式类型不实现所需的成员。
  • CS4028await要求类型具有合适的”GetAwaiter“方法。是否缺少“System”的 using 指令?
  • CS4029无法返回类型为“void”的表达式。
  • CS4030安全属性不能应用于异步方法。
  • CS4031:接口 、类或结构中不允许使用异步方法,该接口、类或结构具有“SecurityCritical”或“SecuritySafeCritical”属性。
  • CS4032await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法,并将其返回类型更改为“”。Task<T>
  • CS4033await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法,并将其返回类型更改为“”。Task
  • CS4034await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法。
  • CS8031转换为任务返回委托的异步 lambda 表达式无法返回值。
  • CS8100await运算符不能用于静态脚本变量初始值设定项。
  • CS8177异步方法不能具有按引用传递的局部变量。
  • CS8178调用方法返回的引用不能跨“”await或“yield边界保留。
  • CS8204对于用作类型目标的 AsyncMethodBuilder 的类型,其 Task 属性应返回目标类型,而不是声明的类型。
  • CS8403具有迭代器块的方法必须为“”async才能返回 IAsyncEnumerable<T>。
  • CS8411异步 foreach 语句无法对类型的变量进行操作,因为类型不包含所需成员的合适公共实例或扩展定义。
  • CS8892方法不会用作入口点,因为找到了同步入口点。
  • CS8935:不允许 匿名方法使用 AsyncMethodBuilder 属性,而无需显式返回类型。
  • CS8940需要泛型类似任务的返回类型,但在“AsyncMethodBuilder”属性中找到的类型不适用。它必须是一个未绑定的 arity 类型的泛型类型,并且其包含类型(如果有)必须是非泛型类型。
  • CS9123&“运算符不应用于异步方法中的参数或局部变量。
  • CS9330MethodImplAttribute.Async“不能手动应用于方法。标记方法“async”。

Await 表达式的要求条件

  • CS1985无法在 catch 子句中等待。
  • CS1986“'await'要求该类型具备合适的'GetAwaiter'方法。”
  • CS1992仅当包含在标记为“await”的修饰符的方法或 lambda 表达式内时,才能使用“async”运算符。
  • CS1995await”运算符只能在初始“from”子句的第一个集合表达式或“join”子句的集合表达式中使用。
  • CS1996无法在 lock 语句内部使用 await。
  • CS4008无法等待“void”。
  • CS4032await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法,并将其返回类型更改为“”。Task<T>
  • CS4033await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法,并将其返回类型更改为“”。Task
  • CS4034await运算符只能在异步方法中使用。请考虑使用“async”修饰符标记此方法。
  • CS8178无法跨“await”或“yield”边界保留此调用返回的引用。
  • CS8411异步 foreach 语句无法对类型的变量进行操作,因为类型不包含所需成员的合适公共实例或扩展定义。
  • CS4001无法等待表达式。
  • CS4003await不能用作异步方法或 lambda 表达式中的标识符。
  • CS4007类型实例无法跨“await”或“yield”边界保留。
  • CS4011await要求”GetAwaiter()“的返回类型具有合适的”IsCompleted“、”OnCompleted“和”GetResult“成员,并实现”INotifyCompletion“或”ICriticalNotifyCompletion”。
  • CS4027类型不实现所需的成员。
  • CS4028“‘await’要求该类型具有合适的‘GetAwaiter’方法。是否缺少‘System’的 using 指令?”
  • CS8100await运算符不能用于静态脚本变量初始值设定项。

以下项说明如何更正每个错误。 有关await运算符和等待器模式的详细信息,请参阅使用 async 和 await 的异步编程

  • async修饰符添加到包含await表达式的方法或 lambda 表达式(CS1992CS4032、CS4033CS4034)。 编译器需要 async 修饰符,以便它可以生成处理异步挂起和恢复的状态机。 此错误的三个变体为正确的返回类型提供特定于上下文的建议。
  • 当以 C# 5 或更早版本为目标时(await),将 catch 表达式移出 块。 从 C# 6 开始,编译器在 await 块和 catch 块中都支持 finally。 此错误不再在 C# 6 及更高版本中生成。
  • 请将表达式 await 移出 lock 语句块CS1996)。 持有锁时异步暂停可能会引发死锁。 锁在线程切换过程中保持,其他代码可能会在等待该锁。
  • 重组 查询表达式,使 await 仅在初始 from 子句的第一个集合表达式或 join 子句的集合表达式中出现(CS1995)。 其他查询子句转换为不支持异步挂起的 lambda 表达式。
  • 更改等待表达式的类型,以便它公开遵循 GetAwaiter()CS1986CS4028)的可访问方法。 该类型可以直接或通过扩展方法实现模式。 如果GetAwaiter方法存在,但缺少usingSystem指令,编译器将生成更具体的 CS4028 消息,而不是 CS1986
  • 确保由 GetAwaiter() 返回的 awaiter 类型具有 IsCompletedOnCompletedGetResult 成员,并实现 INotifyCompletionICriticalNotifyCompletionCS4011CS4027)。 依赖于这些成员的await表达式用于检查完成状态、注册延续和检索结果。
  • 将调用方法的返回类型从void更改为TaskTask<TResult>,以便可以等待结果(CS4008)。 无法等待 void-returning 方法,因为没有用于跟踪完成或传播异常的任务对象。
  • 将 awaited 表达式更改为支持 awaiter 模式 的类型(CS4001)。 类型(例如 intstring和其他内置类型)没有方法 GetAwaiter ,不能直接等待。
  • 在使用 await) 之前,将 ref-returning 方法调用的结果存储在局部变量中。 方法返回的引用不能跨 await 边界保留,因为异步状态机可能会挂起并在不同的线程或上下文中恢复,从而导致该引用失效。
  • 在集合类型上实现IAsyncEnumerable<T>,或添加可返回具有GetAsyncEnumeratorCurrent成员的类型(MoveNextAsync)的可访问方法。 该 await foreach 语句 要求集合类型遵循异步可枚举模式。
  • 请将任何在await方法或 lambda 表达式中的局部变量或参数重命名为asyncCS4003)。 在异步上下文中, await 是上下文关键字,不能用作标识符。
  • await 表达式移出静态脚本变量初始值设定项,并移动到方法主体(CS8100)。 静态初始值设定项在异步上下文外部运行,因此 await 在该位置不可用。
  • 重新构造代码,以便无需在 ref struct 边界上保留 await 的实例(yield)。 异步状态机将局部变量存储在堆上,类型 ref struct 按设计进行堆栈绑定 - 无法安全地跨挂起点移动到堆存储。

异步方法签名要求

  • CS1983由于这是异步方法,因此返回表达式的类型必须为“T”而不是“”。Task<T>
  • CS1994async修饰符只能在具有主体的方法中使用。
  • CS4009返回入口点的 void 或 int 不能是异步的。
  • CS8892方法不会用作入口点,因为找到了同步入口点。
  • CS8935:不允许 匿名方法使用 AsyncMethodBuilder 属性,而无需显式返回类型。
  • CS8940需要泛型类似任务的返回类型,但在“AsyncMethodBuilder”属性中找到的类型不适用。它必须是一个未绑定的 arity 类型的泛型类型,并且其包含类型(如果有)必须是非泛型类型。
  • CS8403具有迭代器块的方法必须是“async”才能返回“{1}”。
  • CS9330'MethodImplAttribute.Async' 不能手动应用于方法。标记方法“async”。
  • CS4005异步方法不能具有指针类型参数。
  • CS4006:在异步方法的参数列表中不允许使用__arglist。
  • CS4010无法将异步 lambda 转换为委托类型。异步 lambda 可能返回 void、Task 或 Task<T>,这些都不能转换为返回类型。
  • CS4012类型参数不能在异步方法或异步 lambda 表达式中声明。
  • CS4015“MethodImplOptions.Synchronized”不能应用于异步方法。
  • CS4016由于这是异步方法,因此返回表达式必须是任务类型而不是类型。
  • CS8031转换为任务返回委托的异步 lambda 表达式无法返回值。
  • CS8204对于要用作某类型的 AsyncMethodBuilder 的类型,其 Task 属性应返回所需的类型,而不是声明的类型。

以下项说明如何更正每个错误。 有关异步方法声明的详细信息,请参阅 async 修饰符和 Async 返回类型

  • 更改返回表达式以匹配异步方法的基础结果类型(CS1983CS4016)。 当异步方法返回 Task<T>时, return 该语句必须提供类型值 T,而不是 Task<T>,因为编译器生成的 状态机 会自动在任务中包装该值。 当方法返回且表达式为Task<T>时,将出现 CS1983;T 涉及返回表达式类型不匹配的一般情况。
  • async从没有正文的方法中删除修饰符,例如抽象方法或接口方法声明(CS1994)。 async修饰符需要一个方法正文,以便编译器可以生成状态机实现。
  • 将异步入口点的返回类型 Task 更改为或 Task<TResult>CS4009)。 从 C# 7.1 开始,方法Main可以是async,但它必须返回TaskTask<int> - async void,而async int 不是有效的入口点签名。
  • 当项目同时包含同步和异步 Main 方法(CS8892)时,请删除或重命名一个入口点。 编译器选择同步入口点,并为它忽略的异步候选项发出此警告。
  • 在应用 [AsyncMethodBuilder] 属性(CS8935)之前,将显式返回类型添加到 lambda 表达式。 编译器无法解析返回类型为推断的匿名方法的构造器类型,因为属性必须在编译时与特定返回类型相匹配。
  • [AsyncMethodBuilder]属性中指定的类型更改为未绑定的、具有一个参数的泛型类型,例如MyTaskMethodBuilder<>,而不是MyTaskMethodBuilder<T>或非泛型类型(CS8940)。 生成器的包含类型(如果有)也必须是非泛型类型。 编译器需要此形状,以便它可以为任何具体的类似任务的返回类型构造生成器。
  • 将手动 [MethodImpl(MethodImplOptions.Async)] 属性替换为 async 方法声明(CS9330)上的关键字。 该 MethodImplOptions.Async 标志保留供内部运行时使用,不能直接在用户代码中应用。
  • async 修饰符添加到包含 迭代器块 并返回 IAsyncEnumerable<T>IAsyncEnumerator<T>CS8403) 的方法。 async如果没有修饰符,编译器会将该方法视为同步迭代器,并且无法生成异步流状态机。
  • 从异步方法中删除指针类型参数(CS4005)。 指针引用固定的内存位置,这些位置无法在异步挂起点安全地保留,因为执行可能会在不同线程上恢复。
  • 从异步方法参数列表(__arglist)中删除。 可变长度参数列表依赖于基于堆栈的调用约定,而该调用约定与堆分配异步状态机不兼容。
  • 从异步方法或异步 lambda 表达式(ref)中删除inout、或ref struct参数,以及类似Span<T>ReadOnlySpan<T>类型参数。 这些参数类型绑定到堆栈,无法在堆分配的异步状态机闭包中安全地捕获。
  • 更改目标委托类型以匹配异步 lambda 的返回类型(CS4010)。 异步 lambda 可能会返回 voidTaskTask<TResult>,而编译器无法将这些转换为预期拥有不同返回类型的任意委托类型。
  • return从分配给返回非泛型Task类型的委托的异步 lambda 表达式中删除该表达式,或将委托类型更改为Func<Task<T>>,以便 lambda 可以返回值(CS8031)。 非泛型 Task返回委托表示无结果的异步操作,因此返回值的类型不匹配。
  • [MethodImpl(MethodImplOptions.Synchronized)]从异步方法中删除属性(CS4015)。 该 Synchronized 选项为整个方法执行获取锁定,但由于异步方法可能在不同线程上挂起和恢复,因此使得锁定机制的语义不明确。
  • 更正自定义 AsyncMethodBuilder 类型,使其 Task 属性返回与异步方法声明的返回类型相同的类型(CS8204)。 编译器使用生成器 Task 的属性获取最终任务对象,因此类型不匹配可防止状态机正常运行。

异步实践方法

  • CS1989不能将异步 lambda 表达式转换为表达式树。
  • CS1991“Type”无法实现“event”,因为它是 Windows 运行时事件,“event”是常规的 .NET 事件。
  • CS1997由于函数是返回值的异步方法,因此返回关键字不得后跟对象表达式。
  • CS1998此异步方法缺少“”await运算符,并且将同步运行。请考虑使用“”await运算符等待非阻塞 API 调用,或“”await Task.Run(...)在后台线程上执行 CPU 绑定工作。
  • CS4014由于此调用未等待,因此在调用完成之前,当前方法的执行将继续执行。请考虑将 await 运算符应用于调用的结果。
  • CS8177异步方法不能使用通过引用的局部变量。
  • CS9123&“运算符不应用于异步方法中的参数或局部变量。
  • CS4029无法返回类型为“void”的表达式。
  • CS4030安全属性不能应用于异步方法。
  • CS4031:接口 、类或结构中不允许使用异步方法,该接口、类或结构具有“SecurityCritical”或“SecuritySafeCritical”属性。

以下项说明如何更正每个错误。 有关详细信息,请参阅使用 async 和 await 和await运算符进行异步编程。

  • await运算符添加到每个返回TaskTask<TResult>结果的调用,或者如果真的需要触发和忘记行为,可以显式地使用_ =放弃结果(CS4014)。 如果没有 await,异步操作引发的任何异常将悄无声息地丢失,调用方法在操作完成之前继续执行,这可能会导致细微的排序问题和正确性错误。
  • return从返回类型为Task(非泛型)的异步方法中删除表达式,或者将返回类型Task<T>更改为当方法需要返回值时(CS1997)。 在返回 Task的异步方法中,编译器生成任务包装器 - 返回值的类型不匹配,因为方法签名不承诺结果。
  • 将至少一个 await 表达式添加到方法正文,或删除 async 修饰符并直接返回任务(CS1998)。 一个没有任何async表达式的await方法完全同步运行,导致了额外的状态机开销。 如果方法有意地包装了一个同步操作,那么移除 async 并显式地返回任务,可以减少此类开销。
  • 重写 lambda 表达式,以便在分配给表达式树类型(如 async)时不使用 Expression<Func<...>>。 表达式树将代码表示为编译器可以分析或转换的数据结构,但无法捕获在表达式树中生成的复杂状态机 async
  • 更改事件实现,以便接口声明和实现类都同意该事件是使用 Windows 运行时语义还是常规 .NET 语义(CS1991)。 此错误适用于 Windows 运行时互操作方案,其中 WinRT 事件无法作为常规 .NET 事件实现,反之亦然。
  • 从引用异步方法中的参数或局部变量的表达式中删除地址运算符&CS9123)。 异步状态机可能会在挂起期间将捕获的变量重新定位到堆,这会使通过 address-of 获取的任何指针失效。
  • 从异步方法中删除按引用局部变量,或确保它们不会跨越 await 边界(CS8177)。 异步状态机在堆分配的闭包中捕获局部变量,并且不能安全地跨挂起点保留对堆栈位置的引用。 在 C# 13 及更高版本中, ref 只要局部变量不跨越 await 边界,就允许在异步方法中使用局部变量,并且不会生成此错误。
  • 去掉返回return方法结果的语句void,或将调用的方法更改为返回值类型方法(CS4029)。 不能使用 return SomeVoidMethod(); ,因为 void 不是可以作为值返回的类型。 删除 return 关键字并将该方法作为独立语句调用,或更改所调用方法的签名以返回具体类型。
  • 删除诸如异步方法([SecurityCritical][SecuritySafeCritical])之类的安全属性,或者从用这些属性标记的类型(async)中的方法中删除修饰符。 代码访问安全注释适用于声明方法,但编译器生成的异步状态机在无法强制实施这些安全注释的单独上下文中运行。