OpCodes.Readonly 字段

定义

指定后面的数组地址操作在运行时不执行类型检查,并且返回可变性受限的托管指针。

public static readonly System.Reflection.Emit.OpCode Readonly;

字段值

注解

下表列出了指令的十六进制和 Microsoft 中间语言 (MSIL) 程序集格式,以及简短的参考摘要:

格式 程序集格式 说明
FE 1E readonly。 指定后续数组地址操作在运行时不执行类型检查,并且返回具有受限可变性的托管指针。

此前缀只能紧跟在 ldelema 指令前面,并调用数组上的特殊 Address 方法。 它对后续操作的影响是双重的:

  1. 在运行时,不执行任何类型检查操作。 请注意,在引用类型数组上使用 时, 和 stelem 指令通常有一个隐式类型检查ldelema。 值类永远不会有运行时类型检查,在这种情况下readonly是无操作。

  2. 验证程序将地址操作的结果视为具有受限可变性的托管指针。

据说指针具有受限的可变性,因为定义类型控制是否可以更改值。 对于不公开任何公共字段或更新值的方法的值类,指针是只读的, (因此前缀的名称) 。 具体而言,表示基元类型的类 (例如 System.Int32) 不公开变量,因此是只读的。

以这种方式受限制的托管指针只能通过以下方式使用:

  • 作为 object 、、ldfldastfldcallconstrained callvirt 指令的参数ldfld

  • 作为pointer指令或其中一个指令的参数ldobjldind

  • source作为指令的参数cpobj

不允许所有其他操作,包括 stobjinitobjmkrefany 操作或任何 stind 指令。

前缀的目的是readonly避免在从泛型代码中的数组中提取元素时检查类型。 例如,表达式 arr[i].m()(其中数组 arr 的元素类型是一种泛型类型,该类型被限制为具有具有方法 m的接口)可能会编译为以下 MSIL。

ldloc arr  
ldloc i  
readonly.  
ldelema !0    // Loads the pointer to the object.  
…             // Load the arguments to the call.  
constrained. !0  
callvirt m  

readonly如果没有前缀,指令ldelema将执行类型检查 !0 为引用类型的情况。 此类型不仅检查效率低下,而且在语义上不正确。 检查ldelema的类型是完全匹配,太强。 如果数组包含类型为 !0 的子类,则上述代码将使类型检查失败。

提取数组元素的地址而不是元素本身,以便有一个句柄,该句柄 arr[i] 适用于值类型和引用类型,因此可以传递给 constrained callvirt 指令。

通常,如果数组包含引用类型的元素,则跳过运行时检查是不安全的。 为了确保安全,必须确保不通过此指针对数组进行任何修改。 验证程序规则确保这一点。 受限的托管指针可以作为实例方法调用的对象传递,因此严格地说,对于值类型,它不是只读的,但值类型不存在类型安全问题。

以下 Emit 方法重载可以使用 readonly opcode:

适用于

产品 版本
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7, 8, 9, 10
.NET Framework 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0