Default Values

Default Values

Default values reside in the Constant metadata table. Three kinds of metadata items can have a default value assigned and therefore can reference the Constant table: fields, method parameters, and properties. A record of the Constant table has three entries:

  • Type (unsigned 1-byte integer)  The type of the constant, one of the ELEMENT_TYPE_* codes. (See Chapter 7.)

  • Parent (coded token of type HasConstant)  A reference to the owner of the constant, a record in the Field, Property, or Param table.

  • Value (offset in the #Blob stream)  A constant value blob.

The current implementation of the common language runtime and ILAsm allows the constant types described in Table 8-1. (As usual, I’ve dropped the ELEMENT_TYPE_ part of the name.)

Table 8-1  Constant Types

Constant Type

ILAsm Notation

Comments

I1

Int8

Signed 1-byte integer.

I2

int16

Signed 2-byte integer.

I4

int32

Signed 4-byte integer.

I8

int64

Signed 8-byte integer.

R4

float32

4-byte floating-point.

R8

float64

8-byte floating-point.

CHAR

char

2-byte Unicode character.

BOOLEAN

bool

1-byte Boolean, true = 1, false = 0.

STRING

<quoted_string>, bytearray

Unicode string.

CLASS

nullref

Null object reference. The value of the constant of this type must be a 4-byte integer containing 0.

The ILAsm syntax for defining the default value of a field is as follows:

<field_def_const> ::= .field <flags> <type>     <name> <const_type> [( <value> )]

The value in parentheses is mandatory for all constant types except nullref. For example:

.field public int32 i = int32(123) .field public static literal bool b = bool(true) .field private float32 f = float32(1.2345) .field public static int16 ii = int16(0xFFE0) .field public object o = nullref

Defining integer and Boolean constants—not to mention nullref—is pretty straightforward, but floating-point constants and strings can present some difficulties.

Floating-point numbers have special cases, such as positive infinity and negative infinity, that cannot be presented textually in simple floating-point format. In these special cases, the floating-point constants can alternatively be represented as integer values with a matching byte count. The integer values are not converted to floating-point values; instead, they represent an exact bit image of the floating-point values. For example:

.field public float32 fPosInf = float32(0x7F800000) .field public float32 fNegInf = float32(0xFF800000) .field public float32 fNAN = float32(0xFFC00000)

Like all other constants, string constants are stored in the #Blob stream. In this regard, they differ from user-defined strings, which are stored in the #US stream. What both kinds of strings have in common is that they are supposed to be Unicode. I say “supposed to be” because the only Unicode-specific restrictions imposed on these strings are that their sizes are reported in Unicode characters and that their byte counts must be even. Otherwise, these strings are simply binary objects and might or might not contain invalid Unicode characters.

Notice that the type of the constant does not need to match the type of the item to which this constant is assigned—in this case, the type of the field.In ILAsm, a string constant can be defined either as a composite quoted string or as a byte array:

.field public static string str1 = "Isn't" + " it " + "marvellous!" .field public static string str2 = bytearray(00 01 FF FE 1A 00 00 )

When a string constant is defined as a composite quoted string, this string is converted to Unicode before being stored in the #Blob stream. In the case of a bytearray definition, the specified byte sequence is stored “as is,” and padded with 1 zero byte if necessary to make the byte count even. In the example shown here, the default value for the str2 field will be padded to bring the byte count to 8 (four Unicode characters). And if the bytes specified in the bytearray are invalid Unicode characters, it will surely be discovered when we try to print the string, but not before.

Assigning default values to fields (and parameters) seems to be such a compelling technique that you might wonder why we did not employ it in the simple sample discussed in Chapter 1, “Simple Sample.” Really, defining the default values is a great way to initialize fields—right? Wrong. Here’s a tricky question. Suppose that we define a member field as follows:

.field public static int32 ii = int32(12345) 

What will the value of the field be when the class is loaded? Correct answer: 0. Why? Because default values specified in the Constant table are not used by the loader to initialize the items to which they are assigned. If we want to initialize a field to its default value, we must explicitly call the respective Reflection method to retrieve the value from metadata and then store this value in the field. This doesn’t sound too nice, but, on the other hand, we should not forget that these are default values rather than initial values, so formally the loader might be right.

Let me remind you once again that literal fields are not true fields. They are not laid out by the loader, and they cannot be directly accessed from IL. From the point of view of metadata, however, literal fields are nevertheless valid fields having valid tokens, which allow the constant values corresponding to these fields to be retrieved by Reflection methods. The common language runtime does not provide an implicit means of accessing the Constant table, which is a pity. It would certainly be much nicer if the JIT compiler would compile the ldsfld instruction into the retrieval of the respective constant value instead of failing, when the ldsfld instruction is applied to a literal field. But such are the facts of life, and I am afraid we cannot do anything about it at the moment.

Given this situation, literal fields without associated Constant records are legal from the loader’s point of view, but they are utterly meaningless. They serve no purpose except to inflate the Field metadata table.

But how do the compilers handle literal fields? If every time a constant from an enumerator—represented, as we know, by a literal field—was used the compiler emitted a call to the Reflection API to get this constant value, one could imagine where it would leave the performance. Most compilers are smarter than that and resolve the literal fields at compile time, replacing references to literal fields with explicit constant values of these fields, so that the literal fields never come into play at run time. So much for having the literal fields in the metadata and devising a special kind of TypeDef for enumerators.

ILAsm, following common language runtime functionality to the letter, allows the definition of the Constant metadata but does nothing about the symbol-to-value resolution at compile time. From the point of view of ILAsm and the runtime, the enumerators are real, as distinctive types, but the symbolic constants listed in the enumerations are not.



Inside Microsoft. NET IL Assembler
Inside Microsoft .NET IL Assembler
ISBN: 0735615470
EAN: 2147483647
Year: 2005
Pages: 147
Authors: SERGE LIDIN

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