通过


与引用参数、变量和返回关联的错误和警告

使用引用变量时,可能会生成以下错误:

  • CS0192字段 readonly 不能用作 refout 值(构造函数除外)
  • CS0199字段 static readonly 不能用作 refout 值(静态构造函数除外)
  • CS0206非引用返回属性或索引器不能用作 outref
  • CS0631refout 在此上下文中无效
  • CS0767无法使用指定的类型参数继承接口,因为它会导致方法包含仅在 refout 上存在不同的重载
  • CS1510refout 值必须是可赋值变量
  • CS1605不能将变量用作 refout 值,因为它为只读
  • CS1623迭代器不能具有 refinout 参数
  • CS1649字段 readonly 的成员不能用作 refout 值(构造函数除外)
  • CS1651静态只读字段的字段不能用作 refout 值(静态构造函数除外)
  • CS1655不能将类型字段用作 refout
  • CS1657不能将变量用作 refout
  • CS1741refout 参数不能有默认值
  • CS1939无法将范围变量作为 outref 参数传递
  • CS1988异步方法不能具有 refinout 参数
  • CS7084无法将 Windows 运行时事件作为 outref 参数传递。
  • CS8196:对隐式类型的 out 变量的引用不允许出现在同一个参数列表中。
  • CS8325"await" 不能在包含 ref 条件运算符的表达式中使用
  • CS8326这两个条件运算符的值必须都是 ref 值或者都不是 ref 值
  • CS8327表达式的类型必须正确,才能匹配备用 ref 值
  • CS8329不能将变量用作 refout 值,因为它是只读变量
  • CS8330变量的成员不能用作 refout 值,因为它是只读变量
  • CS8331无法赋值给变量或将其用作 ref 赋值的右侧,因为它是只读变量
  • CS8332无法赋值给变量的成员或将其用作 ref 赋值的右侧,因为它是只读变量
  • CS8337"ref" 扩展方法的第一个参数必须是值类型或受结构约束的泛型类型。
  • CS8338扩展方法的第一个 "in" 或 "ref readonly" 参数必须是具体的(非泛型)值类型。
  • CS8373ref 赋值的左侧必须是 ref 变量。
  • CS8388out 变量无法声明为 ref 局部变量
  • CS8977无法在具有 "UnmanagedCallersOnly" 特性的方法的签名中使用 "ref"、"in" 或 "out"。
  • CS8986参数的“scoped”修饰符与目标不匹配。
  • CS8987参数的“scoped”修饰符与重写或实现的成员不匹配。
  • CS9061“scoped”修饰符不能与“discard”一起使用。
  • CS9062类型和别名不能命名为“scoped”。
  • CS9063UnscopedRefAttribute 无法应用于此参数,因为它默认处于未作用域状态。
  • CS9065请勿使用“System.Runtime.CompilerServices.ScopedRefAttribute”。请改用“scoped”关键字。
  • CS9066UnscopedRefAttribute 不能应用于具有“scoped”修饰符的参数。
  • CS9072析构变量不能声明为 ref 局部变量
  • CS9101:UnscopedRefAttribute 只能应用于结构或虚拟接口实例方法和属性,不能应用于构造函数或仅初始化成员。
  • CS9102:UnscopedRefAttribute 不能应用于接口实现,因为实现的成员没有此属性
  • CS9104不能在异步方法或异步 Lambda 表达式中使用类型的 using 语句资源。
  • CS9190readonly 修饰符必须在 ref 之后指定。
  • CS9199ref readonly 参数不能具有 Out 特性。

当引用变量被错误地使用时,将生成以下警告:

  • CS9073参数的“scoped”修饰符与目标不匹配。
  • CS9074参数的“scoped”修饰符与重写或实现的成员不匹配。
  • CS9191对应于 ref 参数的自变量的 in 修饰符等效于 in。请考虑改用 in
  • CS9192自变量应随 refin 关键字一起传递。
  • CS9193自变量应为变量,因为它被传递给 ref readonly 参数
  • CS9195自变量应随 in 关键字一起传递
  • CS9196参数的引用类型修饰符与替代或实现的成员中相应的参数不匹配。
  • CS9197参数的引用类型修饰符与隐藏的成员中相应的参数不匹配。
  • CS9198参数的引用类型修饰符与目标中相应的参数不匹配。
  • CS9200ref readonly 参数指定了默认值,但 ref readonly 只应用于引用。请考虑将参数声明为 in
  • CS9201使用前应引用赋值 ref 字段。
  • CS9265字段永远不会被重新分配给,并且将始终具有其默认值(null 引用)

这些错误和警告遵循以下主题:

本文使用“引用变量”一词来作为描述使用 inref readonlyrefout 修饰符中的一个,或 ref 局部变量、ref 中的 ref struct 字段,或 ref 返回声明的参数的一般性术语。 引用变量引用另一个变量,称为“引用对象”。

语法错误

这些错误表示你对引用变量使用了不正确的语法:

  • CS8373ref 赋值的左侧必须是 ref 变量。
  • CS8388out 变量无法声明为 ref 局部变量。
  • CS9190readonly 修饰符必须在 ref 之后指定。

若要更正这些错误,请执行以下步骤:

  • 确保 = ref 运算符的左操作数是引用变量,而不是值表达式或非引用类型的局部变量。 Ref 赋值要求双方都是可以为同一存储位置创建别名的引用变量(CS8373)。
  • 声明引用参数时,应将修饰符写为 ref readonly 而不是 readonly ref。 C# 语言规范要求 ref 关键字位于参数声明中的修饰符之前 readonly ,以在所有引用参数类型(CS9190)中保持一致的语法。
  • 使用 ref 关键字,而不是 out 声明本地引用变量时。 out 是一个独占参数修饰符,指示方法在返回之前必须赋值,而 ref 用于创建别名其他存储位置(CS8388)的局部变量是适当的关键字。 有关引用变量及其语法要求的详细信息,请参阅 参考变量C# 语言规范

引用变量限制

以下错误表示某个引用变量不能用于你已有一个的位置:

  • CS0631refout 在此上下文中无效
  • CS0767无法使用指定的类型参数继承接口,因为它会导致方法包含仅在 refout 上存在不同的重载
  • CS1623迭代器不能具有 refinout 参数
  • CS1741refout 参数不能有默认值
  • CS1939无法将范围变量作为 outref 参数传递
  • CS1988异步方法不能具有 refinout 参数
  • CS7084无法将 Windows 运行时事件作为 outref 参数传递。
  • CS8196:对隐式类型的 变量的引用不允许出现在同一个参数列表中out
  • CS8325"await" 不能在包含 ref 条件运算符的表达式中使用
  • CS8326这两个条件运算符的值必须都是 ref 值或者都不是 ref 值
  • CS8327表达式的类型必须正确,才能匹配备用 ref 值
  • CS8337"ref" 扩展方法的第一个参数必须是值类型或受结构约束的泛型类型。
  • CS8338扩展方法的第一个 "in" 或 "ref readonly" 参数必须是具体的(非泛型)值类型。
  • CS8977无法在具有 "UnmanagedCallersOnly" 特性的方法的签名中使用 "ref"、"in" 或 "out"。
  • CS9072析构变量不能声明为 ref 局部变量
  • CS9104不能在异步方法或异步 Lambda 表达式中使用类型的 using 语句资源。
  • CS9199ref readonly 参数不能具有 Out 特性。

以下警告表示不应使用某个引用变量,并且它可能不安全:

  • CS9196参数的引用类型修饰符与替代或实现的成员中相应的参数不匹配。
  • CS9197参数的引用类型修饰符与隐藏的成员中相应的参数不匹配。
  • CS9198参数的引用类型修饰符与目标中相应的参数不匹配。
  • CS9200ref readonly 参数指定了默认值,但 ref readonly 只应用于引用。请考虑将参数声明为 in
  • CS9201使用前应引用赋值 ref 字段。
  • CS9265字段永远不会被重新分配给,并且将始终具有其默认值(null 引用)

若要更正这些错误,请执行以下步骤:

  • 索引器中删除引用参数。 索引器旨在提供类似数组的访问语法,编译器无法保证安全地追踪通过索引器访问器传递的引用生命周期(CS0631, CS1623)。
  • 迭代器方法中删除引用参数。 迭代器使用状态机在多个调用中延迟执行代码,编译器无法确保引用的变量在挂起和恢复执行(CS1623)的收益率返回边界之间保持有效。
  • 异步方法中删除引用参数。 异步方法可能会在 await 点暂停执行,并在不同的线程上恢复,因此无法保证引用的变量在整个方法的执行(CS1988)中保持有效且可访问。
  • 避免在 ref 条件表达式中使用 await 表达式。 await 操作可能会暂停程序执行,并使条件运算符选择的引用失效,这可能在执行恢复时导致已失效的引用被使用(CS8325)。
  • 确保 ref 条件运算符的两个分支都返回引用,或者两者都不返回引用,并且当两个分支都是引用时,它们必须是相同的类型。 条件运算符必须生成一致的结果类型,无论选择哪个分支(CS8326,CS8327),调用代码都可以安全地使用。
  • ref 中删除默认值和 out 参数。 必须始终在调用站点提供引用参数,才能在参数和现有变量之间建立所需的别名关系,使默认值在语义上毫无意义(CS1741)。
  • 避免在引用同一变量的参数列表中声明隐式类型 out 变量。 编译器必须从方法签名推断变量的类型,同时验证同一表达式中该变量的使用,从而创建循环依赖项(CS8196)。
  • 不要将 LINQ 查询 范围变量作为引用参数传递。 范围变量是编译器生成的迭代变量,其生存期由查询执行模型管理,并且没有可以安全引用的稳定内存位置(CS1939)。
  • 析构对象时,请使用常规值变量,而不是 ref 局部变量。 析构会创建新的变量来接收解构的值,引用变量将尝试对这些临时值进行别名,而不是独立存储这些临时值(CS9072)。
  • 避免实现多个接口,其中方法只因refout参数上的修饰符而异。 C# 语言规范将这些签名视为不同的签名,但不提供一种方法来消除要调用的实现的歧义,因为两者在refout实现边界(CS0767)上共享相同的调用语法。
  • 从被修饰为 System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute 的方法中删除引用参数。 这些方法可从不了解 C# 引用安全规则的非托管代码中调用,并且无法保证在跨托管/非托管边界时(CS8977)正确管理被引用变量的生命周期。
  • 仅对值类型或受值类型约束的泛型类型使用ref修饰符应用于扩展方法的第一个参数。 引用类型已在 CLR 级别通过引用传递,添加ref将创建对引用的引用,而通过使用ref的值类型扩展,您可以修改扩展的实例(CS8337CS8338)。
  • 不要将 Windows 运行时事件作为引用参数传递。 这些事件遵循 Windows 运行时类型系统,该系统具有与 .NET 引用不同的生存期和线程语义,并且不支持 C# 引用参数(CS7084)所需的别名行为。
  • System.Runtime.InteropServices.OutAttribute参数中删除ref readonly。 此属性旨在用于平台调用方案中的封送语义,其中参数的方向仅为出站,这与 ref readonly 的保证相冲突,即参数引用了不会被重新分配的现有数据(CS9199)。
  • 确保在构造函数完成之前,在字段初始值设定项或所有构造函数代码路径中分配类型中的所有 ref 字段。 未初始化的 ref 字段将包含无效引用,如果访问(CS9201,CS9265),可能会导致内存损坏。
  • 匹配方法及其重写的基方法或实现的接口方法之间的引用类型修饰符(refinoutref readonly)。 引用修饰符是派生类型必须遵循的方法签名协定的一部分,以保持可替代性和调用方预期(CS9196CS9197CS9198)。
  • 在提供默认值时,应将参数声明为in而非ref readonlyref readonly 专为调用方传递对现有变量的引用的方案而设计,而 in 参数可以接受值的引用和临时副本,使默认值有意义(CS9200)。

有关允许引用变量的位置的详细信息,请参阅 方法参数迭代器异步编程模式C# 语言规范

unscoped ref 限制

某些位置不允许 unscoped 参数的 ref 限定符:

  • CS9101UnscopedRefAttribute 只能应用于结构实例或虚拟接口方法和属性,不能应用于构造函数或仅初始化成员。
  • CS9102无法将 UnscopedRefAttribute 应用于接口实现,因为已实现的成员没有此属性。

若要更正这些错误,请执行以下步骤:

  • 从结构体构造函数和仅初始化成员中删除 unscoped 修饰符或 System.Diagnostics.CodeAnalysis.UnscopedRefAttribute 属性。 这些成员具有特殊的初始化语义,编译器必须确保任何引用不会超过初始化阶段,并且允许无作用域引用将违反在结构完全可访问之前完成初始化的保证(CS9101)。
  • unscoped当相应的接口方法没有修饰符时,请从接口实现方法中删除修饰符。 未限定的特征会影响方法的协定,关于引用生存期保证,实现必须保持与其实现的接口相同的协定,以确保调用方无论调用哪个具体实现都可以依赖于一致的生存期行为(CS9102)。

关于有作用域和无作用域引用的详细信息,请参阅 方法参数底层结构改进 功能规范。

引用变量需要引用对象

必须向引用参数、引用返回或 ref 本地赋值提供一个变量作为自变量:

  • CS0206非引用返回属性或索引器不能用作 outref
  • CS1510refout 值必须是可赋值变量

警告:

  • CS9191对应于 ref 参数的自变量的 in 修饰符等效于 in。请考虑改用 in
  • CS9192自变量应随 refin 关键字一起传递。
  • CS9193自变量应为变量,因为它被传递给 ref readonly 参数
  • CS9195自变量应随 in 关键字一起传递

若要更正这些错误,请执行以下步骤:

  • 将属性或索引器访问的结果存储在本地变量中,然后再将其作为引用参数传递。 属性索引器是返回值的方法,而不是提供对存储位置的直接访问,引用参数需要具有可别名的稳定内存位置的实际变量(CS0206,CS1510)。
  • 使用 in 修饰符,而不是 ref,在将参数传递给 in 参数时。 虽然由于向后兼容性从技术上有效,但修饰符更清楚地表达了参数是只读的,并且相比复制,更高效地以引用方式传递(CS9191,CS9195)。
  • 将参数传递给需要引用的参数时,添加相应的引用修饰符(refinref readonly)。 省略修饰符可能会导致编译器创建值的临时副本,这效率低下,如果调用代码要求在原始变量(CS9192,CS9193)中反映修改,则可能会导致意外行为。

有关引用参数和按引用传递变量的详细信息,请参阅 方法参数ref 关键字C# 语言规范

可写引用变量需要可写引用对象

可写引用变量要求引用对象也可写。 以下错误表示变量不可写:

  • CS0192字段 readonly 不能用作 refout 值(构造函数除外)
  • CS0199字段 static readonly 不能用作 refout 值(静态构造函数除外)
  • CS1605不能将变量用作 refout 值,因为它为只读
  • CS1649字段 readonly 的成员不能用作 refout 值(构造函数除外)
  • CS1651字段 static readonly 的字段不能用作 refout 值(静态构造函数除外)
  • CS1655不能将类型字段用作 refout
  • CS1657不能将变量用作 refout
  • CS8329不能将变量用作 refout 值,因为它是只读变量
  • CS8330变量的成员不能用作 refout 值,因为它是只读变量
  • CS8331无法赋值给变量或将其用作 ref 赋值的右侧,因为它是只读变量
  • CS8332无法赋值给变量的成员或将其用作 ref 赋值的右侧,因为它是只读变量

若要更正这些错误,请执行以下步骤:

  • 值从只读字段 复制到局部变量,并将局部变量作为 refout 参数传递。 初始化后,除在构造函数中外, 只读字段是不可变的,允许对它们进行可写引用会违反只读所提供的不可变性保证(CS0192, CS0199, CS1649, CS1651)。
  • 使用ref readonlyin参数,而不是refout,当需要按引用传递只读变量、迭代变量或其他不可写值时。 这些修饰符指示该方法将仅读取引用的值,而不尝试修改它。 这与原始变量(CS1605、CS1655CS1657CS8329)的不可变性约束保持一致。
  • 将只读变量的成员复制到局部变量,然后将其作为可写引用传递。 尽管成员本身可能未声明为只读,但它通过只读路径(通过只读字段、in参数或ref readonly本地)进行访问,并且编译器强制实施只读性的可传递性,以防止间接突变只读数据(CS8330,CS8332)。
  • 避免将可写的 ref 赋值用于只读变量、 foreach 迭代变量使用语句资源固定语句变量。 这些变量具有由编译器管理的特殊生存期语义。 变量在其作用域末尾自动完成或释放。 外部引用在被释放后会创建悬空引用(CS8331)。

有关只读语义和引用参数的详细信息,请参阅 readonly 关键字in 参数修饰符ref readonlyC# 语言规范