12 Semantics of Value Types


In contrast to classes, value types (see Partition I, section 8.2.4) are not accessed by using a reference but are stored directly in the location of that type.

RATIONALE

Value types are used to describe the type of small data items. They can be compared to struct (as opposed to pointers to struct) types in C++. Compared to reference types, value types are accessed faster, since there is no additional indirection involved. As elements of arrays they do not require allocating memory for the pointers as well as for the data itself. Typical value types are complex numbers, geometric points, or dates.


Like other types, value types may have fields (static or instance), methods (static, instance, or virtual), properties, events, and nested types. A value type may be converted into a corresponding reference type (its boxed form, a class automatically created for this purpose by the VES when a value type is defined) by a process called boxing. A boxed value type may be converted back into its value type representation, the unboxed form, by a process called unboxing. Value types shall be sealed, and they shall have a base type of either System.ValueType or System.Enum (see the .NET Framework Standard Library Annotated Reference). Value types shall implement zero or more interfaces, but this has meaning only in their boxed form (see Partition II, section 12.3).

Unboxed value types are not considered subtypes of another type, and it is not valid to use the isinst instruction (see Partition III [section 4.6]) on unboxed value types. The isinst instruction may be used for boxed value types. Unboxed value types shall not be assigned the value null and they shall not be compared to null.

Value types support layout control in the same way as reference types do (see Partition II, section 9.7). This is especially important when values are imported from native code.

12.1 Referencing Value Types

The unboxed form of a value type shall be referred to by using the valuetype keyword followed by a type reference. The boxed form of a value type shall be referred to by using the boxed keyword followed by a type reference.

<valueTypeReference> ::=

  

boxed <typeReference> |

 

valuetype <typeReference>

ANNOTATION

Implementation-Specific (Microsoft): For historical reasons, "value class" may be used instead of "valuetype," although the latter is preferred. Microsoft's Common Language Runtime does not support direct references to boxed value types; they should be treated as object instead.


12.2 Initializing Value Types

Like classes, value types may have both instance constructors (see Partition II, section 9.5.1) and type initializers (see Partition II, section 9.5.3). Unlike classes that are automatically initialized to null, however, the following rules constitute the only guarantee about the initialization of (unboxed) value types:

  • Static variables shall be initialized to zero when a type is loaded (see Partition II, section 9.5.3.3), hence statics whose type is a value type are zero-initialized when the type is loaded.

  • Local variables shall be initialized to zero if the appropriate bit in the method header (see Partition II, section 9.5.3.3) is set.

  • Arrays shall be zero-initialized.

  • Instances of classes (i.e., objects) shall be zero-initialized prior to calling their instance constructor.

ANNOTATION

To initialize locals to zero, the assembler syntax is the .local init directive, described in Partition II, 14.4.1.3; and in the file format it is the flag CorILMethod_InitLocals, described in Partition II, section 24.4.4.


RATIONALE

Guaranteeing automatic initialization of unboxed value types is both difficult and expensive, especially on platforms that support thread-local storage and allow threads to be created outside of the CLI and then passed to the CLI for management.


NOTE

Boxed value types are classes and follow the rules for classes.


The instruction initobj (see Partition III [section 4.5]) performs zero-initialization under program control. If a value type has a constructor, an instance of its unboxed type can be created as is done with classes. The newobj instruction (see Partition III [section 4.20]) is used along with the initializer and its parameters to allocate and initialize the instance. The instance of the value type will be allocated on the stack. The Base Class Library provides the method System.Array.Initialize (see the .NET Framework Standard Library Annotated Reference) to zero all instances in an array of unboxed value types.

 

Example (informative): The following code declares and initializes three value type variables. The first variable graphics/ccc.gif is zero-initialized, the second is initialized by calling an instance constructor, and the graphics/ccc.gif third by creating the object on the stack and storing it into the local. .assembly Test { } .assembly extern System.Drawing { .ver 1:0:3102:0 .publickeytoken = (b03f5f7f11d50a3a) } .method public static void Start() { .maxstack 3 .entrypoint .locals init (valuetype [System.Drawing]System.Drawing.Size Zero, valuetype [System.Drawing]System.Drawing.Size Init, valuetype [System.Drawing]System.Drawing.Size Store) // Zero-initialize the local named Zero ldloca Zero // load address of local variable initobj valuetype [System.Drawing]System.Drawing.Size // Call the initializer on the local named Init ldloca Init // load address of local variable ldc.i4 425 // load argument 1 (width) ldc.i4 300 // load argument 2 (height) call instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32) // Create a new instance on the stack and store into Store. Note that // stobj is used here but one could equally well use stloc, stfld, etc. ldloca Store ldc.i4 425 // load argument 1 (width) ldc.i4 300 // load argument 2 (height) newobj instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32) stobj valuetype [System.Drawing]System.Drawing.Size ret }

12.3 Methods of Value Types

Value types may have static, instance, and virtual methods. Static methods of value types are defined and called the same way as static methods of class types. As with classes, both instance and virtual methods of a boxed or unboxed value type may be called using the call instruction. The callvirt instruction shall not be used with unboxed value types (see Partition I, section 8.4.2), but it may be used on boxed value types.

ANNOTATION

Partition I, section 8.4.2 discusses virtual calls (the callvirt instruction, although it is not referred to by that name in that section). However, the statement in the last sentence in the previous paragraph is inaccurate. The callvirt instruction can be used with unboxed value types as well as boxed value types.


Instance and virtual methods of classes shall be coded to expect a reference to an instance of the class as the this pointer. By contrast, instance and virtual methods of value types shall be coded to expect a managed pointer (see Partition I, section 8.2.4) to an unboxed instance of the value type. The CLI shall convert a boxed value type into a managed pointer to the unboxed value type when a boxed value type is passed as the this pointer to a virtual method whose implementation is provided by the unboxed value type.

NOTE

This operation is the same as unboxing the instance, since the unbox instruction (see Partition III [section 4.30]) is defined to return a managed pointer to the value type that shares memory with the original boxed instance.

The following diagrams may help understand the relationship between the boxed and unboxed representations of a value type.


graphics/03inf01.gif

RATIONALE

An important use of instance methods on value types is to change internal state of the instance. This cannot be done if an instance of the unboxed value type is used for the this pointer, since it would be operating on a copy of the value, not the original value: unboxed value types are copied when they are passed as arguments.

Virtual methods are used to allow multiple types to share implementation code, and this requires that all classes that implement the virtual method share a common representation defined by the class that first introduces the method. Since value types can (and in the Base Class Library do) implement interfaces and virtual methods defined on System.Object, it is important that the virtual method be callable using a boxed value type so it can be manipulated as would any other type that implements the interface. This leads to the requirement that the VES automatically unbox value types on virtual calls.


[Table 3-2 shows the type of the this pointer supplied, depending on the type of the instance method, and whether the call or callvirt instruction is used.]

Table 3-2. Type of this, Given CIL Instruction and Declaring Type of Instance Method
 

Value Type (Boxed or Unboxed)

Interface

Class Type

call

managed pointer to value type

illegal

object reference

callvirt

managed pointer to value type

object reference

object reference

 

Example (informative): The following converts an integer of the value type int32 into a string. Recall that int32 graphics/ccc.gif corresponds to the unboxed value type System.Int32 defined in the Base Class Library. graphics/ccc.gif Suppose the integer is declared as: .locals init (int32 x) Then the call is made as shown below: ldloca x // load managed pointer to local variable call instance string valuetype [mscorlib]System.Int32::ToString() However, if System.Object (a class) is used as the type reference rather than System.Int32 graphics/ccc.gif (a value type), the value of x shall be boxed before the call is made, and the code becomes: ldloc x box valuetype [mscorlib]System.Int32 callvirt instance string [mscorlib]System.Object::ToString()


The Common Language Infrastructure Annotated Standard (Microsoft. NET Development Series)
The Common Language Infrastructure Annotated Standard (Microsoft. NET Development Series)
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 121

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net