One directive, .class, defines every kind of type: classes, value types, interfaces and enumerations. A definition is a header (attributes, name, generic parameters, extends, implements) and a body in braces. The header is classHead in the grammar of ilasm and ClassHeader in ECMA-335 II.10.1.
.class public sequential ansi sealed beforefieldinit Point
extends [mscorlib]System.ValueType
implements class [mscorlib]System.IEquatable`1<valuetype Point>
{
.field public int32 X
.field public int32 Y
.pack 4
}
Class attributes
Attributes come between .class and the name, in any order; when two of the same kind of visibility, layout or string format are given, the later one wins. The words are those of the grammar rule classAttr.
| Kind | Words | Meaning |
|---|---|---|
| Visibility | public, private | a top-level type is visible outside the assembly (public) or only inside it (private, the default) |
| Nested accessibility | nested public, nested private, nested family, nested assembly, nested famandassem, nested famorassem | for a type declared inside another type; family is “this type and its derived types”, assembly “this assembly”, famandassem both, famorassem either |
| Layout | auto, sequential, explicit, extended | the runtime chooses the layout (auto, default); fields in declaration order; every field gets an offset (.field [8] int32 x); layout by an ExtendedLayoutAttribute on the type (a .NET addition, value types only, not with .pack or .size) |
| String format | ansi, unicode, autochar | how strings are marshalled to unmanaged code when a method says nothing (ansi is the default) |
| Semantics | interface, value, enum | interface makes an interface (and sets abstract); value and enum are ilasm’s own words (below) |
| Inheritance | abstract, sealed | cannot be instantiated; cannot be derived from. All value types are sealed |
| Special handling | beforefieldinit, specialname, rtspecialname, serializable, import, windowsruntime | the type initializer need not run before the first static method call; names with a meaning for tools or the runtime (rtspecialname is accepted and ignored by ilasm); the type is serializable; imported from COM; a Windows Runtime type |
| Raw flags | flags(n) | replaces all attribute bits with the number n |
Whether the definition is a class, a value type or an interface is decided from these words and the base type: interface gives an interface; a type that extends System.ValueType (and is not System.Enum itself) is a value type; anything else is a class. ilasm helps in two ways. If there is no extends, the base becomes System.Object (or System.ValueType when value is written, System.Enum when enum is written), taken from the assembly that ilasm finds as the base-library reference; an interface has no base. This default is switched off with the option /NOAUTOINHERIT. A value type that is not sealed gets a warning and is made sealed.
Base type and interfaces
extends takes one type specification; implements takes a comma-separated list of them. An interface has no extends and lists the interfaces it requires after implements; an enum cannot implement an interface. Both clauses can use any form of type reference, so a generic instance is allowed (implements class [mscorlib]System.IComparable`1<!0>).
A custom attribute on an interface implementation is written .interfaceimpl type Interface followed by a .custom declaration in the body of the class (.interfaceimpl is an ilasm directive that the standard does not list).
.class interface public abstract auto ansi IShape
{
.method public hidebysig newslot abstract virtual
instance float64 Area() cil managed
{
}
}
Nested types and namespaces
A .class inside another class defines a nested type; its attributes use nested followed by an accessibility (never public or private alone). Outside the enclosing type it is named with a slash: Outer/Inner. The metadata has no namespaces: a type name is one string that contains dots. ilasm lets you write a .namespace block as a convenience; its name becomes the prefix of every type name inside the braces, and blocks can nest.
.namespace My.App
{
.class public auto ansi Outer
{
.class nested public auto ansi Inner
{
}
}
}
My.App.Outer and My.App.Outer/Inner.Generic types
Generic parameters follow the name in angle brackets. Each parameter is written attributes (constraints) name, with both parts optional.
| Part | Meaning |
|---|---|
+, - | the parameter is covariant or contravariant (only on interfaces and delegates) |
class | the argument must be a reference type |
valuetype | the argument must be a value type other than Nullable<T> |
.ctor | the argument must have a public parameterless constructor, or be a value type |
byreflike | the argument may be a by-reference-like type such as a Span<T> (a .NET addition; not in the standard) |
flags(n) | the attribute bits given as a number |
(type, …) | the constraints: types the argument must derive from or implement; the type before the name may be a comma-separated list |
class and valuetype are not allowed together; a name occurs once. The name of the type carries the number of parameters after a grave accent: Dict`2. Custom attributes of a parameter are attached with .param type name (or the index in brackets: .param type [1]) followed by a .custom, and an extra constraint with custom attributes by .param constraint name, type (an ilasm directive that the standard does not list).
.class public auto ansi Dict`2<(I1, I2) K, (Base) V>
extends [mscorlib]System.Object
{
}
.class interface public abstract auto ansi IProducer`1<+ T>
{
}
.class public auto ansi Box`1<class .ctor T>
{
}
Instance layout
With sequential layout the fields follow each other in declaration order. With explicit layout every field names its offset in brackets, in bytes from the start of the fields this type defines itself (.field [0] int32 a); fields may overlap, but an object reference must not overlap a value. .pack n limits the alignment of the fields to n bytes (n is 0, 1, 2, 4, 8, 16, 32, 64 or 128; 0 means the platform default) and is not allowed with explicit layout; .size n sets a minimum size in bytes, which pads the type (a value type must stay under 1 MB).
What a class body holds
| Item | Meaning | Page |
|---|---|---|
.method, .field, .property, .event | members | Methods, Fields and data |
.class | a nested type | this page |
.custom, .permission, .permissionset | attributes and security declarations of the type | Custom attributes |
.data | data in the file, usually the target of a field’s at | Fields and data |
.override | makes a method implement an interface or base-class method of another name (see below) | this page |
.pack, .size | layout | this page |
.param type, .param constraint, .interfaceimpl | attributes of generic parameters and of interface implementations | this page |
.line, #line, .language, and the # lines | debug information and preprocessing | Preprocessor |
.export | declares an exported type (the deprecated form of .class extern) | Assemblies and modules |
The .override directive in a class body has two forms: .override Type::Name with callconv returntype Type::Name(parameters) and, since the generics, .override method callconv returntype Type::Name<[arity]>(parameters) with method callconv returntype Type::Name<[arity]>(parameters): the second method is the implementation of the first. Inside a method body the short form .override Type::Name makes the current method the implementation of that method. The runtime addendum to the standard describes how overrides work for default interface methods, static virtual members and covariant return types.
Global fields and methods, outside any .class, belong to a special type named <Module> that has no base type, and in the metadata they are members of it.
Not described
Known, not yet covered
- Reference: the semantic rules of the standard for classes (II.10, II.11 to II.14: what a valid hierarchy is, the rules for interfaces, delegates, enums, vectors and arrays) and the runtime addendum’s sections on default interface methods, static virtual members, covariant returns, ref fields and the module initializer are not summarised here; the grammar is the subject.