A Custom Attribute has the following syntax diagram:
All binary values are stored in little-endian format (except PackedLen items, which are used only as counts for the number of bytes to follow in a UTF8 string). If there are no fields, parameters, or properties specified the entire attribute is represented as an empty blob.
CustomAttrib starts with a Prolog – an unsigned int16, with value 0x0001.
Next comes a description of the fixed arguments for the constructor method. Their number and type is found by examining that constructor's row in the MethodDef table; this information is not repeated in the CustomAttrib itself. As the syntax diagram shows, there can be zero or more FixedArgs. (Note that VARARG
constructor methods are not allowed in the definition of Custom Attributes.)
Next is a description of the optional "named" fields and properties. This starts with NumNamed – an unsigned int16 giving the number of "named" properties or fields that follow. Note that NumNamed shall always be present. A value of zero indicates that there are no "named" properties or fields to follow (and of course, in this case, the CustomAttrib shall end immediately after NumNamed). In the case where NumNamed is non-zero, it is followed by NumNamed repeats of NamedArgs.
The format for each FixedArg depends upon whether that argument is an SZARRAY
or not – this is shown in the lower and upper paths, respectively, of the syntax diagram. So each FixedArg is either a single Elem, or NumElem repeats of Elem.
(SZARRAY
is the single byte 0x1D, and denotes a vector – a single-dimension array with a lower bound of zero.)
NumElem is an unsigned int32 specifying the number of elements in the SZARRAY
, or 0xFFFFFFFF to indicate that the value is null.
An Elem takes one of the forms in this diagram, as follows:
-
If the parameter kind is simple (first line in the above diagram) (bool, char, float32, float64, int8, int16, int32, int64, unsigned int8, unsigned int16, unsigned int32 or unsigned int64) then the 'blob' contains its binary value (Val). (A bool is a single byte with value 0 (false) or 1 (true); char is a two-byte Unicode character; and the others have their obvious meaning.) This pattern is also used if the parameter kind is an enum – simply store the value of the enum's underlying integer type.
-
If the parameter kind is string, (middle line in above diagram) then the blob contains a SerString – a PackedLen count of bytes, followed by the UTF8 characters. If the string is null, its PackedLen has the value 0xFF (with no following characters). If the string is empty (""), then PackedLen has the value 0x00 (with no following characters).
-
If the parameter kind is
System.Type
, (also, the middle line in above diagram) its value is stored as a SerString (as defined in the previous paragraph), representing its canonical name. The canonical name is its full type name, followed optionally by the assembly where it is defined, its version, culture and public-key-token. If the assembly name is omitted, the CLI looks first in the current assembly, and then in the system library (mscorlib
); in these two special cases, it is permitted to omit the assembly-name, version, culture and public-key-token. -
If the parameter kind is
System.Object
, (third line in the above diagram) the value stored represents the "boxed" instance of that value-type. In this case, the blob contains the actual type's FieldOrPropType (see below), followed by the argument's unboxed value. [Note: it is not possible to pass a value of null in this case. end note] -
If the type is a boxed simple value type (bool, char, float32, float64, int8, int16, int32, int64, unsigned int8, unsigned int16, unsigned int32 or unsigned int64) then FieldOrPropType is immediately preceded by a byte containing the value 0x51 . The FieldOrPropType shall be exactly one of:
ELEMENT_TYPE_BOOLEAN
,ELEMENT_TYPE_CHAR
,ELEMENT_TYPE_I1
,ELEMENT_TYPE_U1
,ELEMENT_TYPE_I2
,ELEMENT_TYPE_U2
,ELEMENT_TYPE_I4
,ELEMENT_TYPE_U4
,ELEMENT_TYPE_I8
,ELEMENT_TYPE_U8
,ELEMENT_TYPE_R4
,ELEMENT_TYPE_R8
,ELEMENT_TYPE_STRING
. A single-dimensional, zero-based array is specified as a single byte 0x1D followed by the FieldOrPropTypeof the element type. (See §II.23.1.16) An enum is specified as a single byte 0x55 followed by a SerString.
A NamedArg is simply a FixedArg (discussed above) preceded by information to identify which field or property it represents. [Note: Recall that the CLI allows fields and properties to have the same name; so we require a means to disambiguate such situations. end note]
-
FIELD
is the single byte 0x53. -
PROPERTY
is the single byte 0x54.
The FieldOrPropName is the name of the field or property, stored as a SerString (defined above). A number of examples involving custom attributes are contained in Annex B of Partition VI.