与 ref 安全相关的错误和警告

违反引用变量安全规则时,可能会生成以下错误:

  • CS8166无法通过引用返回参数,因为它不是 ref 参数
  • CS8167无法通过引用返回参数成员,因为它不是 refout 参数
  • CS8168无法通过引用返回局部变量,因为它不是 ref 局部变量
  • CS8169无法通过引用返回局部变量成员,因为它不是 ref 局部变量
  • CS8345字段或自动实现的属性不能是某种类型,除非它是实例成员的ref struct
  • CS8351ref 条件运算符的分支不能引用具有不兼容声明范围的变量
  • CS8374无法引用赋值,源的转义范围比目标更窄。
  • CS9075无法按引用返回参数,因为它的范围限定为当前方法
  • CS9076无法通过引用参数成员返回,因为它的范围限定为当前方法
  • CS9077无法通过 ref 参数按引用返回参数,它只能在 return 语句中返回
  • CS9078无法通过 ref 参数按引用返回参数的成员,它只能在 return 语句中返回
  • CS9079无法将源引用赋值给目标,因为源只能通过返回语句逸出当前方法。
  • CS9096无法将源引用赋值给目标,因为源的值的转义范围比目标更广,从而允许通过目标分配具有比源更窄的转义范围的值。

违反引用变量安全规则时,将生成以下警告:

  • CS9080在此上下文中使用变量可能会在其声明范围之外公开引用的变量
  • CS9081此上下文中类型的 stackalloc 表达式的结果可能在包含方法之外公开
  • CS9082本地由引用返回,但已初始化为引用无法返回的值
  • CS9083成员由引用返回,但已初始化为引用无法返回的值
  • CS9084结构体成员通过引用返回“this”或其他实例成员
  • CS9085此 ref 将源分配给目标,但源的逃逸范围比目标要窄。
  • CS9086ref 条件运算符的分支引用声明范围不兼容的变量
  • CS9087这会按引用返回一个参数,但它不是 ref 参数
  • CS9088这会按引用返回参数,但范围限定为当前方法
  • CS9089这会按引用返回不是 refout 参数的参数成员
  • CS9090此代码通过引用返回参数成员,该成员的作用域限定于当前方法
  • CS9091这会按引用返回局部变量,但它不是 ref 局部变量
  • CS9092这会按引用返回局部变量的成员,但它不是 ref 局部变量
  • CS9093此引用将源变量分配给目标变量,但源变量只能通过 return 语句才能从当前方法中返回。
  • CS9094这会通过 ref 参数按引用返回参数,但它只能在 return 语句中安全返回
  • CS9095这会通过 ref 参数按引用返回参数的成员,但它只能在 return 语句中安全返回
  • CS9097此 ref 将源赋值给目标,但源的值转义范围比目标具有较宽的值转义范围,允许通过目标分配其转义范围较源更窄的值。

返回具有不兼容作用域的引用

当变量的生存期不超出方法的范围时,编译器会阻止你返回对变量的引用。 尝试通过引用未用 ref 声明的参数、局部变量或其作用域仅限于当前方法内的成员时,会导致这些错误发生。

Errors:

  • CS8166无法通过引用返回参数,因为它不是 ref 参数
  • CS8167无法通过引用返回参数成员,因为它不是 refout 参数
  • CS8168无法通过引用返回局部变量,因为它不是 ref 局部变量
  • CS8169无法通过引用返回局部变量成员,因为它不是 ref 局部变量
  • CS9075无法按引用返回参数,因为它的范围限定为当前方法
  • CS9076无法通过引用参数成员返回,因为它的范围限定为当前方法
  • CS9077无法通过 ref 参数按引用返回参数,它只能在 return 语句中返回
  • CS9078无法通过 ref 参数按引用返回参数的成员,它只能在 return 语句中返回

警告:

  • CS9087这会按引用返回一个参数,但它不是 ref 参数
  • CS9088这会按引用返回参数,但范围限定为当前方法
  • CS9089这会按引用返回不是 refout 参数的参数成员
  • CS9090这会通过引用,返回一个属于参数的成员,该参数仅限于当前方法范围内
  • CS9091这会按引用返回局部变量,但它不是 ref 局部变量
  • CS9092这会按引用返回局部变量的成员,但它不是 ref 局部变量
  • CS9094这会通过 ref 参数按引用返回参数,但它只能在 return 语句中安全返回
  • CS9095这会通过 ref 参数按引用返回参数的成员,但它只能在 return 语句中安全返回

若要解决以下错误,

  • 更改方法签名以使用关键字声明参数ref,而不是按值传递参数,这样就可以安全地返回参数的存储位置,因为调用方控制变量的生存期(CS8166、CS8167CS9087、CS9089)。
  • 对于局部变量,请将它们声明为ref的 ref 局部变量,方式是通过引用返回的表达式或 ref 参数进行赋值,这可确保局部变量引用的存储具有足够的生命周期,而不是创建具有方法作用域生命周期的新变量(CS8168、CS8169CS9091CS9092)。
  • 使用scoped修饰符声明参数时,请避免通过引用返回该参数,因为scoped修饰符明确限制参数的引用逃离方法,以防止潜在的悬空引用(CS9075CS9076CS9088CS9090)。
  • 如果需要返回来自ref参数的引用,请使用直接return ref语句,而不是将引用分配给另一个ref参数并返回该引用,因为编译器只能通过直接返回语句(CS9077、CS9078CS9094CS9095)跟踪转义范围。

有关 ref 安全规则的详细信息,请参阅有关 ref 返回 的文章和 有关 ref 安全上下文的 C# 标准部分。

具有不兼容作用域的引用分配

编译器阻止引用赋值操作,其中源变量的转义范围比目标变量的范围更窄。 执行 ref 赋值时,会从目标创建到源存储位置的引用。 如果源在目标之前可能超出范围,那么目标将会引用已失效的内存。

Errors:

  • CS8374无法引用赋值,源的转义范围比目标更窄。
  • CS9079无法将源引用赋值给目标,因为源只能通过 return 语句转义当前方法。
  • CS9096无法将源引用赋值给目标,因为源的值转义范围比目标更广,因此允许通过转义范围比源更窄的值目标进行赋值。

警告:

  • CS9085这会将源引用赋值给目标,但源的转义范围比目标要窄。
  • CS9093这会将源引用赋值给目标,但源只能通过 return 语句对当前方法进行转义。
  • CS9097这会将源引用赋值给目标,但源的值转义范围比目标更广,因此可以通过转义范围比源更窄的值目标进行赋值。

若要解决以下错误,

  • 重新构造代码,使 ref 赋值中的源变量至少具有与目标变量一样宽的转义范围,这可确保目标引用在其整个生存期内保持有效,并防止悬空引用(CS8374,CS9085)。
  • 当变量只能通过 return 语句离开方法时,请勿将其分配给通过其他方式访问的变量。 示例包括存储在字段中或通过 ref 类型的参数返回。 这些操作违反了只能在返回语句中使用源的限制(CS9079CS9093)。
  • 对于涉及值转义范围的 ref 赋值,请确保源的值转义范围不大于目标范围,因为不匹配将使你能够通过目标引用分配范围较窄的值,从而可能创建对短生存期值的引用(CS9096,CS9097)。

有关 ref 安全规则的详细信息,请参阅有关 ref 返回 的文章和 有关 ref 安全上下文的 C# 标准部分。

处理作用域冲突和条件运算符

编译器跟踪变量如何通过各种操作逃出其声明的作用域。 当在可能导致引用的变量超出其有效生命周期的上下文(包括 ref 条件运算符和 stackalloc 表达式)中使用变量时,会发生这些错误。

Errors:

  • CS8351ref 条件运算符的分支不能引用具有不兼容声明范围的变量

警告:

  • CS9080在此上下文中使用变量可能会在其声明范围之外公开引用的变量
  • CS9081此上下文中类型的 stackalloc 表达式的结果可能在包含方法之外公开
  • CS9086ref 条件运算符的分支引用具有不兼容声明范围的变量

若要解决以下错误,

  • 修改 ref 条件运算符(?:具有ref返回的运算符),使 true 和 false 分支都引用具有兼容声明范围的变量,这意味着这两个变量必须具有扩展到至少相同的范围级别的生存期,从而防止条件表达式可能返回无效的引用(CS8351,CS9086)。
  • 在表达式或方法调用中使用变量时,请确保上下文不允许引用的变量在声明范围之外转义,这通常意味着避免将作用域变量传递给可能捕获或存储超出预期生存期的方法或表达式(CS9080)。
  • 对于 stackalloc 表达式,请避免将结果分配给变量或在包含方法外部访问堆栈分配内存的上下文中使用,因为当方法返回并访问它之后会导致未定义的行为(CS9081)时,会自动释放堆栈分配的内存。

有关详细信息,请参阅有关 ref 返回的文章、 有关内存使用情况的文章,以及 有关 ref 安全上下文的 C# 标准部分。

结构体成员和字段限制

编译器对结构成员和字段强制实施特殊规则,以防止悬空引用。 当结构成员返回对实例状态的引用或字段具有需要特殊处理的类型时,会发生这些错误。

Errors:

  • CS8345字段或自动实现的属性不能是类型,除非它是 ref struct 的实例成员。

警告:

  • CS9082局部变量以引用方式返回,但被初始化为一个无法通过引用返回的值
  • CS9083成员通过引用返回,但已初始化为不能通过引用返回的值
  • CS9084结构成员通过引用返回“this”或其他实例成员

若要解决以下错误,

  • 确保字段和自动实现的属性与 ref 类似类型(如 Span<T>ref struct 类型)仅声明为 ref 结构中的实例成员,而不是在常规结构或类中,因为类似 ref 的类型只能安全地存在于堆栈中,ref 结构提供必要的生存期保证(CS8345)。
  • 通过方法引用返回局部变量时,请验证局部变量是否是从具有足够转义范围的源(例如 ref 参数或能返回引用的方法调用)初始化的,而不是从将导致对短期存储创建引用的值类型表达式或局部变量初始化的(CS9082, CS9083)。
  • 在结构实例方法或属性中,避免按引用返回 this 或任何实例字段,因为结构是经常复制的值类型,并且返回对实例成员的引用可能会创建对方法返回后销毁的临时副本的引用(CS9084)。

有关详细信息,请参阅有关 ref 结构类型和有关 ref 安全上下文的 C# 标准部分的文章。