OpCodes.Readonly 字段


指定后面的数组地址操作在运行时不执行类型检查,并且返回可变性受限的托管指针。Specifies that the subsequent array address operation performs no type check at run time, and that it returns a managed pointer whose mutability is restricted.

public: static initonly System::Reflection::Emit::OpCode Readonly;
public static readonly System.Reflection.Emit.OpCode Readonly;
 staticval mutable Readonly : System.Reflection.Emit.OpCode
Public Shared ReadOnly Readonly As OpCode 



下表列出了指令的十六进制和 Microsoft 中间语言(MSIL)程序集格式以及简短的参考摘要:The following table lists the instruction's hexadecimal and Microsoft intermediate language (MSIL) assembly format, along with a brief reference summary:

格式Format 程序集格式Assembly Format 说明Description
FE 1EFE 1E 只读.readonly. 指定后续数组地址操作在运行时不执行类型检查,并且它返回具有受限可变性的托管指针。Specify that the subsequent array address operation performs no type check at run time, and that it returns a managed pointer with restricted mutability.

此前缀只能出现在 ldelema 指令之前,并调用针对数组的特殊 @no__t 1 方法。This prefix can only appear immediately preceding the ldelema instruction and calls to the special Address method on arrays. 其对后续操作的影响是双重的:Its effect on the subsequent operation is twofold:

  1. 在运行时,不执行任何类型检查操作。At run time, no type check operation is performed. 请注意,在引用类型数组上使用时,通常会检查 ldelema 和 @no__t 1 说明。Note that there is normally an implicit type check for the ldelema and stelem instructions when used on reference type arrays. 对于值类,从来不会进行运行时类型检查,因此在这种情况下 readonly 是非操作。There is never a run-time type check for value classes, so readonly is a no-op in that case.

  2. 验证程序将操作地址操作的结果视为具有受限可变性的托管指针。The verifier treats the result of the address-of operation as a managed pointer with restricted mutability.

指针被称为具有受限的可变性,因为定义类型控制值是否可转变。The pointer is said to have restricted mutability because the defining type controls whether the value can be mutated. 对于公开无公共字段或更新就地值的方法的值类,该指针为只读(因此为前缀的名称)。For value classes that expose no public fields or methods that update the value in place, the pointer is read-only (hence the name of the prefix). 特别是,表示基元类型的类(例如,System.object)不公开赋值函数,因此它们是只读的。In particular, the classes representing primitive types (for example, System.Int32) do not expose mutators and thus are read-only.

以这种方式限制的托管指针只能通过以下方式使用:A managed pointer restricted in this fashion can be used only in the following ways:

  • 作为 @no__t 的 @no__t 参数,ldfldastfldcall 或 @ no__t 说明。As the object parameter for the ldfld, ldflda, stfld, call, orconstrained callvirt instructions.

  • 作为 @no__t 1 指令的 @no__t 0 参数或 @no__t 指令之一。As the pointer parameter to the ldobj instruction or to one of the ldind instructions.

  • 作为 @no__t @no__t 指令的参数。As the source parameter to the cpobj instruction.

不允许所有其他操作,包括 stobjinitobjmkrefany 操作或任何 @no__t 3 指令。All other operations disallowed, including the stobj, initobj, or mkrefany operations, or any of the stind instructions.

@No__t 前缀的目的是在从泛型代码中的数组获取元素时避免类型检查。The purpose of the readonly prefix is to avoid a type check when fetching an element from an array in generic code. 例如,表达式 arr[i].m(),其中 array @no__t 的元素类型是一个泛型类型,该类型被约束为具有方法 m 的接口,可能会编译为以下 MSIL。For example, the expression arr[i].m(), where the element type of the array arr is a generic type that has been constrained to have an interface with method m, might compile to the following MSIL.

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

如果没有 readonly 前缀,则 @no__t 的指令将在!0是引用类型的情况下执行类型检查。Without the readonly prefix, the ldelema instruction would perform a type check in the case where !0 was a reference type. 这种类型的检查不仅低效,而且它在语义上不正确。Not only is this type check inefficient, but it is semantically incorrect. @No__t-0 的类型检查是完全匹配,这太强。The type check for ldelema is an exact match, which is too strong. 如果数组持有类型为!0的子类,则上述代码将无法通过类型检查。If the array held subclasses of type !0, the code above would fail the type check.

提取数组元素的地址,而不是元素本身,以获得适用于值类型和引用类型的 arr[i] 的句柄,因此可以将该句柄传递给 @no__t 1 指令。The address of the array element is fetched, instead of the element itself, in order to have a handle for arr[i] that works for both value types and reference types, and thus can be passed to the constrained callvirt instruction.

通常,如果数组持有引用类型的元素,则跳过运行时检查是不安全的。In general it would be unsafe to skip the run-time check if the array held elements of a reference type. 为安全起见,需要确保不通过此指针对数组进行任何修改。To be safe, it is necessary to ensure that no modifications to the array are made through this pointer. 验证程序规则确保这一点。The verifier rules ensure this. 受限制的托管指针可以作为实例方法调用的对象进行传递,因此对于值类型而言,并不严格地说是只读的,但对于值类型,不存在任何类型安全问题。The restricted managed pointer can be passed as the object of instance method calls, so it is not strictly speaking read-only for value types, but there is no type safety problem for value types.

以下 @no__t 0 方法重载可以使用 @no__t 操作码:The following Emit method overload can use the readonly opcode:

  • ILGenerator.Emit(OpCode)ILGenerator.Emit(OpCode)