使用引用变量时,可能会生成以下错误:
-
CS0192:字段
readonly不能用作ref或out值(构造函数除外) -
CS0199:字段
static readonly不能用作ref或out值(静态构造函数除外) -
CS0206:非引用返回属性或索引器不能用作
out或ref值 -
CS0631:
ref和out在此上下文中无效 -
CS0767:无法使用指定的类型参数继承接口,因为它会导致方法包含仅在
ref和out上存在不同的重载 -
CS1510:
ref或out值必须是可赋值变量 -
CS1605:不能将变量用作
ref或out值,因为它为只读 -
CS1623:迭代器不能具有
ref、in或out参数 -
CS1649:字段
readonly的成员不能用作ref或out值(构造函数除外) -
CS1651:静态只读字段的字段不能用作
ref或out值(静态构造函数除外) -
CS1655:不能将类型字段用作
ref或out值 -
CS1657:不能将变量用作
ref或out值 -
CS1741:
ref或out参数不能有默认值 -
CS1939:无法将范围变量作为
out或ref参数传递 -
CS1988:异步方法不能具有
ref、in或out参数 -
CS7084:无法将 Windows 运行时事件作为
out或ref参数传递。 - CS8196:对隐式类型的 out 变量的引用不允许出现在同一个参数列表中。
-
CS8325:"
await" 不能在包含ref条件运算符的表达式中使用 - CS8326:这两个条件运算符的值必须都是 ref 值或者都不是 ref 值
- CS8327:表达式的类型必须正确,才能匹配备用 ref 值
-
CS8329:不能将变量用作
ref或out值,因为它是只读变量 -
CS8330:变量的成员不能用作
ref或out值,因为它是只读变量 -
CS8331:无法赋值给变量或将其用作
ref赋值的右侧,因为它是只读变量 -
CS8332:无法赋值给变量的成员或将其用作
ref赋值的右侧,因为它是只读变量 -
CS8337:"
ref" 扩展方法的第一个参数必须是值类型或受结构约束的泛型类型。 -
CS8338:扩展方法的第一个 "
in" 或 "ref readonly" 参数必须是具体的(非泛型)值类型。 -
CS8373:
ref赋值的左侧必须是 ref 变量。 -
CS8388:
out变量无法声明为 ref 局部变量 -
CS8977:无法在具有 "UnmanagedCallersOnly" 特性的方法的签名中使用 "
ref"、"in" 或 "out"。 - CS8986: 参数的“scoped”修饰符与目标不匹配。
- CS8987: 参数的“scoped”修饰符与重写或实现的成员不匹配。
- CS9061:“scoped”修饰符不能与“discard”一起使用。
- CS9062: 类型和别名不能命名为“scoped”。
- CS9063: UnscopedRefAttribute 无法应用于此参数,因为它默认处于未作用域状态。
- CS9065: 请勿使用“System.Runtime.CompilerServices.ScopedRefAttribute”。请改用“scoped”关键字。
- CS9066: UnscopedRefAttribute 不能应用于具有“scoped”修饰符的参数。
- CS9072:析构变量不能声明为 ref 局部变量
- CS9101:UnscopedRefAttribute 只能应用于结构或虚拟接口实例方法和属性,不能应用于构造函数或仅初始化成员。
- CS9102:UnscopedRefAttribute 不能应用于接口实现,因为实现的成员没有此属性。
-
CS9104:不能在异步方法或异步 Lambda 表达式中使用类型的
using语句资源。 -
CS9190:
readonly修饰符必须在ref之后指定。 -
CS9199:
ref readonly参数不能具有 Out 特性。
当引用变量被错误地使用时,将生成以下警告:
- CS9073: 参数的“scoped”修饰符与目标不匹配。
- CS9074: 参数的“scoped”修饰符与重写或实现的成员不匹配。
-
CS9191:对应于
ref参数的自变量的in修饰符等效于in。请考虑改用in。 -
CS9192:自变量应随
ref或in关键字一起传递。 -
CS9193:自变量应为变量,因为它被传递给
ref readonly参数 -
CS9195:自变量应随
in关键字一起传递 - CS9196:参数的引用类型修饰符与替代或实现的成员中相应的参数不匹配。
- CS9197:参数的引用类型修饰符与隐藏的成员中相应的参数不匹配。
- CS9198:参数的引用类型修饰符与目标中相应的参数不匹配。
-
CS9200:为
ref readonly参数指定了默认值,但ref readonly只应用于引用。请考虑将参数声明为in。 - CS9201:使用前应引用赋值 ref 字段。
- CS9265: 字段永远不会被重新分配给,并且将始终具有其默认值(null 引用)
这些错误和警告遵循以下主题:
- 语法不正确:声明或用法的语法无效。
-
语言构造,其中
ref变量无效:某些 C# 习惯用法不允许变量。 通常,这是因为无法可靠地执行 ref 安全分析。 - 需要引用变量时使用了值表达式:用作引用变量的表达式必须是变量,不能是值表达式。
- 引用只读变量的可写引用变量:无法通过可写引用传递对只读变量的引用。
本文使用“引用变量”一词来作为描述使用 in、ref readonly、ref 或 out 修饰符中的一个,或 ref 局部变量、ref 中的 ref struct 字段,或 ref 返回声明的参数的一般性术语。 引用变量引用另一个变量,称为“引用对象”。
语法错误
这些错误表示你对引用变量使用了不正确的语法:
-
CS8373:
ref赋值的左侧必须是 ref 变量。 -
CS8388:
out变量无法声明为 ref 局部变量。 -
CS9190:
readonly修饰符必须在ref之后指定。
若要更正这些错误,请执行以下步骤:
- 确保
= ref运算符的左操作数是引用变量,而不是值表达式或非引用类型的局部变量。 Ref 赋值要求双方都是可以为同一存储位置创建别名的引用变量(CS8373)。 - 声明引用参数时,应将修饰符写为
ref readonly而不是readonly ref。 C# 语言规范要求ref关键字位于参数声明中的修饰符之前readonly,以在所有引用参数类型(CS9190)中保持一致的语法。 - 使用
ref关键字,而不是out声明本地引用变量时。out是一个独占参数修饰符,指示方法在返回之前必须赋值,而ref用于创建别名其他存储位置(CS8388)的局部变量是适当的关键字。 有关引用变量及其语法要求的详细信息,请参阅 参考变量 和 C# 语言规范。
引用变量限制
以下错误表示某个引用变量不能用于你已有一个的位置:
-
CS0631:
ref和out在此上下文中无效 -
CS0767:无法使用指定的类型参数继承接口,因为它会导致方法包含仅在
ref和out上存在不同的重载 -
CS1623:迭代器不能具有
ref、in或out参数 -
CS1741:
ref或out参数不能有默认值 -
CS1939:无法将范围变量作为
out或ref参数传递 -
CS1988:异步方法不能具有
ref、in或out参数 -
CS7084:无法将 Windows 运行时事件作为
out或ref参数传递。 - 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语句资源。 -
CS9199:
ref readonly参数不能具有 Out 特性。
以下警告表示不应使用某个引用变量,并且它可能不安全:
- CS9196:参数的引用类型修饰符与替代或实现的成员中相应的参数不匹配。
- CS9197:参数的引用类型修饰符与隐藏的成员中相应的参数不匹配。
- CS9198:参数的引用类型修饰符与目标中相应的参数不匹配。
-
CS9200:为
ref 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的值类型扩展,您可以修改扩展的实例(CS8337、CS8338)。 - 不要将 Windows 运行时事件作为引用参数传递。 这些事件遵循 Windows 运行时类型系统,该系统具有与 .NET 引用不同的生存期和线程语义,并且不支持 C# 引用参数(CS7084)所需的别名行为。
- 从System.Runtime.InteropServices.OutAttribute参数中删除
ref readonly。 此属性旨在用于平台调用方案中的封送语义,其中参数的方向仅为出站,这与ref readonly的保证相冲突,即参数引用了不会被重新分配的现有数据(CS9199)。 - 确保在构造函数完成之前,在字段初始值设定项或所有构造函数代码路径中分配类型中的所有
ref字段。 未初始化的 ref 字段将包含无效引用,如果访问(CS9201,CS9265),可能会导致内存损坏。 - 匹配方法及其重写的基方法或实现的接口方法之间的引用类型修饰符(
ref、in、out、ref readonly)。 引用修饰符是派生类型必须遵循的方法签名协定的一部分,以保持可替代性和调用方预期(CS9196、 CS9197、 CS9198)。 - 在提供默认值时,应将参数声明为
in而非ref readonly。ref readonly专为调用方传递对现有变量的引用的方案而设计,而in参数可以接受值的引用和临时副本,使默认值有意义(CS9200)。
有关允许引用变量的位置的详细信息,请参阅 方法参数、 迭代器、 异步编程模式和 C# 语言规范。
unscoped ref 限制
某些位置不允许 unscoped 参数的 ref 限定符:
- CS9101: UnscopedRefAttribute 只能应用于结构实例或虚拟接口方法和属性,不能应用于构造函数或仅初始化成员。
- CS9102: 无法将 UnscopedRefAttribute 应用于接口实现,因为已实现的成员没有此属性。
若要更正这些错误,请执行以下步骤:
- 从结构体构造函数和仅初始化成员中删除
unscoped修饰符或 System.Diagnostics.CodeAnalysis.UnscopedRefAttribute 属性。 这些成员具有特殊的初始化语义,编译器必须确保任何引用不会超过初始化阶段,并且允许无作用域引用将违反在结构完全可访问之前完成初始化的保证(CS9101)。 -
unscoped当相应的接口方法没有修饰符时,请从接口实现方法中删除修饰符。 未限定的特征会影响方法的协定,关于引用生存期保证,实现必须保持与其实现的接口相同的协定,以确保调用方无论调用哪个具体实现都可以依赖于一致的生存期行为(CS9102)。
关于有作用域和无作用域引用的详细信息,请参阅 方法参数 和 底层结构改进 功能规范。
引用变量需要引用对象
必须向引用参数、引用返回或 ref 本地赋值提供一个变量作为自变量:
-
CS0206:非引用返回属性或索引器不能用作
out或ref值 -
CS1510:
ref或out值必须是可赋值变量
警告:
-
CS9191:对应于
ref参数的自变量的in修饰符等效于in。请考虑改用in。 -
CS9192:自变量应随
ref或in关键字一起传递。 -
CS9193:自变量应为变量,因为它被传递给
ref readonly参数 -
CS9195:自变量应随
in关键字一起传递
若要更正这些错误,请执行以下步骤:
- 将属性或索引器访问的结果存储在本地变量中,然后再将其作为引用参数传递。 属性和索引器是返回值的方法,而不是提供对存储位置的直接访问,引用参数需要具有可别名的稳定内存位置的实际变量(CS0206,CS1510)。
- 使用
in修饰符,而不是ref,在将参数传递给in参数时。 虽然由于向后兼容性从技术上有效,但 修饰符更清楚地表达了参数是只读的,并且相比复制,更高效地以引用方式传递( CS9191,CS9195)。 - 将参数传递给需要引用的参数时,添加相应的引用修饰符(
refin或ref readonly)。 省略修饰符可能会导致编译器创建值的临时副本,这效率低下,如果调用代码要求在原始变量(CS9192,CS9193)中反映修改,则可能会导致意外行为。
有关引用参数和按引用传递变量的详细信息,请参阅 方法参数、 ref 关键字和 C# 语言规范。
可写引用变量需要可写引用对象
可写引用变量要求引用对象也可写。 以下错误表示变量不可写:
-
CS0192:字段
readonly不能用作ref或out值(构造函数除外) -
CS0199:字段
static readonly不能用作ref或out值(静态构造函数除外) -
CS1605:不能将变量用作
ref或out值,因为它为只读 -
CS1649:字段
readonly的成员不能用作ref或out值(构造函数除外) -
CS1651:字段
static readonly的字段不能用作ref或out值(静态构造函数除外) -
CS1655:不能将类型字段用作
ref或out值 -
CS1657:不能将变量用作
ref或out值 -
CS8329:不能将变量用作
ref或out值,因为它是只读变量 -
CS8330:变量的成员不能用作
ref或out值,因为它是只读变量 -
CS8331:无法赋值给变量或将其用作
ref赋值的右侧,因为它是只读变量 -
CS8332:无法赋值给变量的成员或将其用作
ref赋值的右侧,因为它是只读变量
若要更正这些错误,请执行以下步骤:
- 将 值从只读字段 复制到局部变量,并将局部变量作为
ref或out参数传递。 初始化后,除在构造函数中外, 只读字段是不可变的,允许对它们进行可写引用会违反只读所提供的不可变性保证(CS0192, CS0199, CS1649, CS1651)。 - 使用
ref readonly或in参数,而不是ref或out,当需要按引用传递只读变量、迭代变量或其他不可写值时。 这些修饰符指示该方法将仅读取引用的值,而不尝试修改它。 这与原始变量(CS1605、CS1655、CS1657、CS8329)的不可变性约束保持一致。 - 将只读变量的成员复制到局部变量,然后将其作为可写引用传递。 尽管成员本身可能未声明为只读,但它通过只读路径(通过只读字段、
in参数或ref readonly本地)进行访问,并且编译器强制实施只读性的可传递性,以防止间接突变只读数据(CS8330,CS8332)。 - 避免将可写的 ref 赋值用于只读变量、 foreach 迭代变量、 使用语句资源或 固定语句变量。 这些变量具有由编译器管理的特殊生存期语义。 变量在其作用域末尾自动完成或释放。 外部引用在被释放后会创建悬空引用(CS8331)。
有关只读语义和引用参数的详细信息,请参阅 readonly 关键字、in 参数修饰符、ref readonly 和 C# 语言规范。