Types describe values and specify a contract (see Partition 1, section 8.6) that all values of that type shall support. Because the CTS supports Object-Oriented Programming (OOP) as well as functional and procedural programming languages, it deals with two kinds of entities: Objects and Values. Values are simple bit patterns for things like integers and floats; each value has a type that describes both the storage that it occupies and the meanings of the bits in its representation, and also the operations that may be performed on that representation. Values are intended for representing the corresponding simple types in programming languages like C, and also for representing non-objects in languages like C++ and Java. Objects have rather more to them than do values. Each object is self-typing; that is, its type is explicitly stored in its representation. It has an identity that distinguishes it from all other objects, and it has slots that store other entities (which may be either objects or values). While the contents of its slots may be changed, the identity of an object never changes. There are several kinds of Objects and Values, as shown in Figure 2-1. Figure 2-1. Type System8.1 Relationship to Object-Oriented Programming
The term type is often used in the world of value-oriented programming to mean data representation. In the object-oriented world it usually refers to behavior rather than to representation. In the CTS, type is used to mean both of these things: two entities have the same type if and only if they have both compatible representations and behaviors. Thus, in the CTS, if one type is derived from a base type, then instances of the derived type may be substituted for instances of the base type because both the representation and the behavior are compatible. In the CTS, unlike some OOP languages, two objects that have fundamentally different representations have different types. Some OOP languages use a different notion of type. They consider two objects to have the same type if they respond in the same way to the same set of messages. This notion is captured in the CTS by saying that the objects implement the same interface. Similarly, some OOP languages (e.g., Smalltalk) consider message passing to be the fundamental model of computation. In the CTS, this corresponds to calling virtual methods (see Partition I, section 8.4.4), where the signature of the virtual method plays the role of the message. The CTS itself does not directly capture the notion of "typeless programming." That is, there is no way to call a non-static method without knowing the type of the object. Nevertheless, typeless programming can be implemented based on the facilities provided by the reflection package (see the .NET Framework Standard Library Annotated Reference) if it is implemented.
Subclass means that there is an inheritance relationship that the subclass inherits from its parent class. If A is a subclass of B, then although it is true that A is a subtype of B, the inverse is not necessarily true it is possible for a subtype of something not to be its subclass.
8.2 Values and TypesTypes describe values. All places where values are stored, passed, or operated upon have a type e.g., all variables, parameters, evaluation stack locations, and method results. The type defines the allowable values and the allowable operations supported by the values of the type. All operators and functions have expected types for each of the values accessed or used. A value can be of more than one type. A value that supports many interfaces is an example of a value that is of more than one type, as is a value that inherits from another. 8.2.1 Value Types and Reference TypesThere are two kinds of types: Value Types and Reference Types.
8.2.2 Built-in TypesThe data types in Table 2-1 are an integral part of the CTS and are supported directly by the Virtual Execution System (VES). They have special encoding in the persisted metadata:
So although on the surface it may seem inconsistent to choose all signed integers except the unsigned byte for the CLS, it was, in the end, felt to be the more useful choice.
8.2.3 Classes, Interfaces, and ObjectsEvery value has an exact type that fully describes the value. A type fully describes a value if it unambiguously defines the value's representation and the operations defined on the value. For a Value Type, defining the representation entails describing the sequence of bits that make up the value's representation. For a Reference Type, defining the representation entails describing the location and the sequence of bits that make up the value's representation. A method describes an operation that may be performed on values of an exact type. Defining the set of operations allowed on values of an exact type entails specifying named methods for each operation. Some types are only a partial description e.g., interface types. Interface types describe a subset of the operations and none of the representation, and hence, cannot be an exact type of any value. Hence, while a value has only one exact type, it may also be a value of many other types as well. Furthermore, since the exact type fully describes the value, it also fully specifies all of the other types that a value of the exact type can have. While it is true that every value has an exact type, it is not always possible to determine the exact type by inspecting the representation of the value. In particular, it is never possible to determine the exact type of a value of a Value Type. Consider two of the built-in Value Types, 32-bit signed and unsigned integers. While each type is a full specification of their respective values i.e., an exact type there is no way to derive that exact type from a value's particular 32-bit sequence. For some values, called objects, it is always possible to determine the exact type from the value. Exact types of objects are also called object types. Objects are values of Reference Types, but not all Reference Types describe objects. Consider a value that is a pointer to a 32-bit integer, a kind of Reference Type. There is no way to discover the type of the value by examining the pointer bits, hence it is not an object. Now consider the built-in CTS Reference Type System.String (see the .NET Framework Standard Library Annotated Reference). The exact type of a value of this type is always determinable by examining the value, hence values of type System.String are objects and System.String is an object type.
8.2.4 Boxing and Unboxing of ValuesFor every Value Type, the CTS defines a corresponding Reference Type called the boxed type. The reverse is not true: Reference Types do not in general have a corresponding Value Type. The representation of a value of a boxed type (a boxed value) is a location where a value of the Value Type may be stored. A boxed type is an object type, and a boxed value is an object. All Value Types have an operation called box. Boxing a value of any Value Type produces its boxed value i.e., a value of the corresponding boxed type containing a bit copy of the original value. All boxed types have an operation called unbox. Unboxing results in a managed pointer to the bit representation of the value. Notice that interfaces and inheritance are defined only on Reference Types. Thus, while a Value Type definition (see Partition I, section 8.9.7) can specify both interfaces that shall be implemented by the Value Type and the class (System.ValueType or System.Enum) from which it inherits, these apply only to boxed values.
CLS Rule 3: The CLS does not include boxed value types. NOTE In lieu of boxed types, use System.Object, System.ValueType or System.Enum, as appropriate. (See the .NET Framework Standard Library Annotated Reference.) CLS (consumer): Need not import boxed value types. CLS (extender): Need not provide syntax for defining or using boxed value types. CLS (framework): Shall not use boxed value types in their publicly exposed aspects.
8.2.5 Identity and Equality of ValuesThere are two binary operators defined on all pairs of values, identity and equality, that return a Boolean result. Both of these operators are mathematical equivalence operators; i.e., they are:
In addition, identity always implies equality, but not the reverse; i.e., the equality operator need not be the same as the identity operator as long as two identical values are also equal values. To understand the difference between these operations, consider three variables whose type is System.String, where the arrow is intended to mean "is a reference to": The values of the variables are identical if the locations of the sequences of characters are the same i.e., there is in fact only one string in memory. The values stored in the variables are equal if the sequences of characters are the same. Thus, the values of variables A and B are identical, the values of variables A and C as well as B and C are not identical, and the values of all three of A, B, and C are equal.
8.2.5.1 IdentityThe identity operator is defined by the CTS as follows.
Identity is implemented on System.Object via the ReferenceEquals method. 8.2.5.2 EqualityFor value types, the equality operator is part of the definition of the exact type. Definitions of equality should obey the following rules:
Equality is implemented on System.Object via the Equals method. NOTE Although two floating point NaNs are defined by IEC 60559:1989 to always compare as unequal, the contract for System.Object.Equals requires that overrides must satisfy the requirements for an equivalence operator. Therefore, System.Double.Equals and System.Single.Equals return True when comparing two NaNs, while the equality operator returns False in that case, as required by the standard.
8.3 LocationsValues are stored in locations. A location can hold a single value at a time. All locations are typed. The type of the location embodies the requirements that shall be met by values that are stored in the location. Examples of locations are local variables and parameters. More importantly, the type of the location specifies the restrictions on usage of any value that is loaded from the location. For example, a location can hold values of potentially many exact types as long as all of the values are assignment compatible with the type of the location (see below). All values loaded from a location are treated as if they are of the type of the location. Only operations valid for the type of the location may be invoked even if the exact type of the value stored in the location is capable of additional operations. 8.3.1 Assignment Compatible LocationsA value may be stored in a location only if one of the types of the value is assignment compatible with the type of the location. A type is always assignment compatible with itself. Assignment compatibility can often be determined at compile time, in which case there is no need for testing at runtime. Assignment compatibility is described in detail in Partition I, section 8.7. 8.3.2 CoercionSometimes it is desirable to take a value of a type that is not assignment compatible with a location and convert the value to a type that is assignment compatible. This is accomplished through coercion of the value. Coercion takes a value of a particular type and a desired type and attempts to create a value of the desired type that has equivalent meaning to the original value. Coercion can result in representation changes as well as type changes, hence coercion does not necessarily preserve the identity of two objects. There are two kinds of coercion: widening, which never loses information, and narrowing, in which information may be lost. An example of a widening coercion would be coercing a value that is a 32-bit signed integer to a value that is a 64-bit signed integer. An example of a narrowing coercion is the reverse: coercing a 64-bit signed integer to a 32-bit signed integer. Programming languages often implement widening coercions as implicit conversions, whereas narrowing coercions usually require an explicit conversion. Some widening coercion is built directly into the VES operations on the built-in types (see Partition I, section 12.1). All other coercion shall be explicitly requested. For the built-in types, the CTS provides operations to perform widening coercions with no runtime checks and narrowing coercions with runtime checks. 8.3.3 CastingSince a value can be of more than one type, a use of the value needs to clearly identify which of its types is being used. Since values are read from locations that are typed, the type of the value which is used is the type of the location from which the value was read. If a different type is to be used, the value is cast to one of its other types. Casting is usually a compile-time operation, but if the compiler cannot statically know that the value is of the target type, a runtime cast check is done. Unlike coercion, a cast never changes the actual type of an object, nor does it change the representation. Casting preserves the identity of objects. For example, a runtime check may be needed when casting a value read from a location that is typed as holding values of a particular interface. Since an interface is an incomplete description of the value, casting that value to be of a different interface type will usually result in a runtime cast check. 8.4 Type MembersAs stated above, the type defines the allowable values and the allowable operations supported by the values of the type. If the allowable values of the type have a substructure, that substructure is described via fields or array elements of the type. If there are operations that are part of the type, those operations are described via methods on the type. Fields, array elements, and methods are called members of the type. Properties and events are also members of the type. 8.4.1 Fields, Array Elements, and ValuesThe representation of a value (except for those of built-in types) can be subdivided into sub-values. These sub-values are either named, in which case they are called fields, or they are accessed by an indexing expression, in which case they are called array elements. Types that describe values composed of array elements are array types. Types that describe values composed of fields are compound types. A value cannot contain both fields and array elements, although a field of a compound type may be an array type and an array element may be a compound type. Array elements and fields are typed, and these types never change. All of the array elements shall have the same type. Each field of a compound type may have a different type. 8.4.2 MethodsA type may associate operations with the type or with each instance of the type. Such operations are called methods. A method is named and has a signature (see Partition I, section 8.6.1) that specifies the allowable types for all of its arguments and for its return value, if any. A method that is associated only with the type itself (as opposed to a particular instance of the type) is called a static method (see Partition I, section 8.4.3). A method that is associated with an instance of the type is either an instance method or a virtual method (see Partition I, section 8.4.4). When they are invoked, instance and virtual methods are passed the instance on which this invocation is to operate (known as this or a this pointer). The fundamental difference between an instance method and a virtual method is in how the implementation is located. An instance method is invoked by specifying a class and the instance method within that class. The object passed as this may be null (a special value indicating that no instance is being specified) or an instance of any type that inherits (see Partition I, section 8.9.8) from the class that defines the method. A virtual method may also be called in this manner. This occurs, for example, when an implementation of a virtual method wishes to call the implementation supplied by its parent class. The CTS allows this to be null inside the body of a virtual method. RATIONALE Allowing a virtual method to be called with a non-virtual call eliminates the need for a "call super" instruction and allows version changes between virtual and non-virtual methods. It requires CIL generators to insert explicit tests for a null pointer if they don't want the null this pointer to propagate to called methods. A virtual or instance method may also be called by a different mechanism, a virtual call. Any type that inherits from a type that defines a virtual method may provide its own implementation of that method (this is known as overriding, see Partition I, section 8.10.4). It is the exact type of the object (determined at runtime) that is used to decide which of the implementations to invoke.
8.4.3 Static Fields and Static MethodsTypes may declare locations that are associated with the type rather than any particular value of the type. Such locations are static fields of the type. As such, static fields declare a location that is shared by all values of the type. Just like non-static (instance) fields, a static field is typed and that type never changes. Static fields are always restricted to a single application domain basis (see Partition I, section 12.5), but they may also be allocated on a per-thread basis. Similarly, types may also declare methods that are associated with the type rather than with values of the type. Such methods are static methods of the type. Since an invocation of a static method does not have an associated value on which the static method operates, there is no this pointer available within a static method. 8.4.4 Virtual MethodsAn object type may declare any of its methods as virtual. Unlike other methods, each exact type that implements the type may provide its own implementation of a virtual method. A virtual method may be invoked through the ordinary method call mechanism that uses the static type, method name, and types of parameters to choose an implementation, in which case the this pointer may be null. In addition, however, a virtual method may be invoked by a special mechanism (a virtual call) that chooses the implementation based on the dynamically detected type of the instance used to make the virtual call rather than the type statically known at compile time. Virtual methods may be marked final (see Partition I, section 8.10.2). 8.5 NamingNames are given to entities of the type system so that they can be referred to by other parts of the type system or by the implementations of the types. Types, fields, methods, properties, and events have names. With respect to the type system, values, locals, and parameters do not have names. An entity of the type system is given a single name; e.g., there is only one name for a type. 8.5.1 Valid NamesAll comparisons [of CLI names] are done on a byte-by-byte (i.e., case-sensitive, locale-independent, also known as code-point comparison) basis. Where names are used to access built-in VES-supplied functionality (for example, the class initialization method) there is always an accompanying indication on the definition so as not to build in any set of reserved names.
CLS Rule 4: Assemblies shall follow Annex 7 of Technical Report 15 of the Unicode Standard 3.0 (ISBN 0-201-61633-5) governing the set of characters permitted to start and be included in identifiers, available on-line at http://www.unicode.org/unicode/reports/tr15/tr15-18.html. Identifiers shall be in the canonical format defined by Unicode Normalization Form C. For CLS purposes, two identifiers are the same if their lowercase mappings (as specified by the Unicode locale-insensitive, 1-1 lowercase mappings) are the same. That is, for two identifiers to be considered different under the CLS they shall differ in more than simply their case. However, in order to override an inherited definition the CLI requires the precise encoding of the original declaration be used. NOTE CLS (consumer): Need not consume types that violate CLS rule 4, but shall have a mechanism to allow access to named items that use one of its own keywords as the name. CLS (extender): Need not create types that violate CLS rule 4. Shall provide a mechanism for defining new names that obey these rules but are the same as a keyword in the language. CLS (framework): Shall not export types that violate CLS rule 4. Should avoid the use of names that are commonly used as keywords in programming languages (see Partition V, Annex D).
8.5.2 Assemblies and ScopingGenerally, names are not unique. Names are collected into groupings called scopes. Within a scope, a name may refer to multiple entities as long as they are of different kinds (methods, fields, nested types, properties, and events) or have different signatures.
CLS Rule 5: All names introduced in a CLS-compliant scope shall be distinct independent of kind, except where the names are identical and resolved via overloading. That is, while the CTS allows a single type to use the same name for a method and a field, the CLS does not. CLS Rule 6: Fields and nested types shall be distinct by identifier comparison alone, even though the CTS allows distinct signatures to be distinguished. Methods, properties, and events that have the same name (by identifier comparison) shall differ by more than just the return type, except as specified in CLS Rule 39. NOTE CLS (consumer): Need not consume types that violate these rules after ignoring any members that are marked as not CLS-compliant. CLS (extender): Need not provide syntax for defining types that violate these rules. CLS (framework): Shall not mark types as CLS-compliant if they violate these rules unless they mark sufficient offending items within the type as not CLS-compliant so that the remaining members do not conflict with one another. A named entity has its name in exactly one scope. Hence, to identify a named entity, both a scope and a name need to be supplied. The scope is said to qualify the name. Types provide a scope for the names in the type; hence types qualify the names in the type. For example, consider a compound type Point that has a field named x. The name "field x" by itself does not uniquely identify the named field, but the qualified name "field x in type Point" does.
Since types are named, the names of types are also grouped into scopes. To fully identify a type, the type name shall be qualified by the scope that includes the type name. Type names are scoped by the assembly that contains the implementation of the type. An assembly is a configured set of loadable code modules and other resources that together implement a unit of functionality. The type name is said to be in the assembly scope of the assembly that implements the type. Assemblies themselves have names that form the basis of the CTS naming hierarchy. The type definition:
8.5.2a Enumeration Types
The CTS supports an enum (also known as an enumeration type), an alternate name for an existing type. For purposes of matching signatures an enum shall not be the same as the underlying type. Instances of an enum, however, shall be assignment compatible with the underlying type and vice versa. That is: no cast (see Partition I, section 8.3.3) or coercion (see Partition I, section 8.3.2) is required to convert from the enum to the underlying type, nor are they required from the underlying type to the enum. An enum is considerably more restricted than a true type:
The underlying type shall be a built-in integer type. Enums shall derive from System.Enum, hence they are value types. Like all value types, they shall be sealed (see Partition I, section 8.9.8.2).
CLS Rule 7: The underlying type of an enum shall be a built-in CLS integer type. CLS Rule 8: There are two distinct kinds of enums, indicated by the presence or absence of the System.FlagsAttribute (see the .NET Framework Standard Library Annotated Reference) custom attribute. One represents named integer values; the other, named bit flags that can be combined to generate an unnamed value. The value of an enum is not limited to the specified values. CLS Rule 9: Literal static fields (see Partition I, section 8.6.1) of an enum shall have the type of the enum itself. NOTE CLS (consumer): Shall accept definition of enums that follow these rules, but need not distinguish flags from named values. CLS (extender): Same as consumer. Extender languages are encouraged to allow the authoring of enums, but need not do so. CLS (framework): Shall not expose enums that violate these rules, and shall not assume that enums have only the specified values (even for enums that are named values). 8.5.3 Visibility, Accessibility, and SecurityTo refer to a named entity in a scope, both the scope and the name in the scope shall be visible (see Partition I, section 8.5.3.1). Visibility is determined by the relationship between the entity that contains the reference (the referent) and the entity that contains the name being referenced. Consider the following pseudo-code: class A { int32 IntInsideA; } class B inherits from A { void method X(int32, int32) { IntInsideA := 15; } } If we consider the reference to the field IntInsideA in class A:
There are two fundamental questions that need to be answered in order to decide whether the referent is allowed to access the referenced entity. The first is whether the name of the referenced entity is visible to the referent. If it is visible, then there is a separate question of whether the referent is accessible (see Partition I, section 8.5.3.2). Access to a member of a type is permitted only if all three of the following conditions are met:
8.5.3.1 Visibility of TypesOnly type names, not member names, have controlled visibility. Type names fall into one of the following three categories
8.5.3.2 Accessibility of MembersA type scopes all of its members, and it also specifies the accessibility rules for its members. Except where noted, accessibility is decided based only on the statically visible type of the member being referenced and the type and assembly that is making the reference. The CTS supports seven different rules for accessibility:
In general, a member of a type can have any one of these accessibility rules assigned to it. There are two exceptions, however:
RATIONALE Languages including C++ allow this "widening" of access. Restricting access would provide an incorrect illusion of security since simply casting an object to the base class (which occurs implicitly on method call) would allow the method to be called despite the restricted accessibility. To prevent overriding a virtual method, use final (see Partition I, section 8.10.2) rather than relying on limited accessibility.
CLS Rule 10: Accessibility shall not be changed when overriding inherited methods, except when overriding a method inherited from a different assembly with accessibility family-or-assembly. In this case the override shall have accessibility family. NOTE CLS (consumer): Need not accept types that widen access to inherited virtual methods. CLS (extender): Need not provide syntax to widen access to inherited virtual methods. CLS (frameworks): Shall not rely on the ability to widen access to a virtual method, either in the exposed portion of the framework or by users of the framework. 8.5.3.3 Security PermissionsAccess to members is also controlled by security demands that may be attached to an assembly, type, method, property, or event. Security demands are not part of a type contract (see Partition I, section 8.6), and hence are not inherited. There are two kinds of demands:
Only one demand of each kind may be attached to any item. Attaching a security demand to an assembly implies that it is attached to all types in the assembly unless another demand of the same kind is attached to the type. Similarly, a demand attached to a type implies the same demand for all members of the type unless another demand of the same kind is attached to the member. For additional information, see Declarative Security in Partition II, section 19, and the classes in the System.Security namespace in the .NET Framework Standard Library Annotated Reference. 8.5.3.4 Nested TypesA type (called a nested type) can be a member of an enclosing type. A nested type has the same visibility as the enclosing type and has an accessibility as would any other member of the enclosing type. This accessibility determines which other types may make references to the nested type. That is, for a class to define a field or array element of a nested type, have a method that takes a nested type as a parameter or returns one as value, etc., the nested type shall be both visible and accessible to the referencing type. A nested type is part of the enclosing type, so its methods have access to all members of its enclosing type, as well as family access to members of the type from which it inherits (see Partition I, section 8.9.8). The names of nested types are scoped by their enclosing type, not their assembly (only top-level types are scoped by their assembly). There is no requirement that the names of nested types be unique within an assembly. 8.6 ContractsContracts are named. They are the shared assumptions on a set of signatures (see Partition I, section 8.6.1) between all implementers and all users of the contract. The signatures are the part of the contract that can be checked and enforced.
Contracts are not types; rather they specify requirements on the implementation of types. Types state which contracts they abide by i.e., which contracts all implementations of the type shall support. An implementation of a type can be verified to check that the enforceable parts of a contract, the named signatures, have been implemented. The kinds of contracts are:
8.6.1 SignaturesSignatures are the part of a contract that can be checked and automatically enforced. Signatures are formed by adding constraints to types and other signatures. A constraint is a limitation on the use of or allowed operations on a value or location. Example constraints would be whether a location may be overwritten with a different value or whether a value may ever be changed. All locations have signatures, as do all values. Assignment compatibility requires that the signature of the value, including constraints, is compatible with the signature of the location, including constraints. There are four fundamental kinds of signatures: type signatures, location signatures, parameter signatures, and method signatures.
CLS Rule 11: All types appearing in a signature shall be CLS-compliant. CLS Rule 12: The visibility and accessibility of types and members shall be such that types in the signature of any member shall be visible and accessible whenever the member itself is visible and accessible. For example, a public method that is visible outside its assembly shall not have an argument whose type is visible only within the assembly. NOTE CLS (consumer): Need not accept types whose members violate these rules. CLS (extender): Need not provide syntax to violate these rules. CLS (framework): Shall not violate this rule in its exposed types and their members. The following sections describe the various kinds of signatures. These descriptions are cumulative: the simplest signature is a type signature; a location signature is a type signature plus (optionally) some additional attributes; and so forth.
8.6.1.1 Type SignaturesType signatures define the constraints on a value and its usage. A type, by itself, is a valid type signature. The type signature of a value cannot be determined by examining the value or even by knowing the class type of the value. The type signature of a value is derived from the location signature (see below) of the location from which the value is loaded. Normally the type signature of a value is the type in the location signature from which the value is loaded.
RATIONALE The distinction between a Type Signature and a Location Signature (below) is not currently useful. It is made because certain constraints, such as "constant," are constraints on values, not locations. Future versions of this standard, or non-standard extensions, may introduce type constraints, thus making the distinction meaningful. 8.6.1.2 Location SignaturesAll locations are typed. This means that all locations have a location signature, which defines constraints on the location, its usage, and on the usage of the values stored in the location. Any valid type signature is a valid location signature. Hence, a location signature contains a type and may additionally contain the constant constraint. The location signature may also contain location constraints that give further restrictions on the uses of the location. The location constraints are:
CLS Rule 13: The value of a literal static is specified through the use of field initialization metadata (see Partition II). A CLS-compliant literal must have a value specified in field initialization metadata that is of exactly the same type as the literal (or of the underlying type, if that literal is an enum). NOTE CLS (consumer): Must be able to read field initialization metadata for static literal fields and inline the value specified when referenced. Consumers may assume that the type of the field initialization metadata is exactly the same as the type of the literal field; i.e., a consumer tool need not implement conversions of the values. CLS (extender): Must avoid producing field initialization metadata for static literal fields in which the type of the field initialization metadata does not exactly match the type of the field. CLS (framework): Should avoid the use of syntax specifying a value of a literal that requires conversion of the value. Note that compilers may do the conversion themselves before persisting the field initialization metadata resulting in a CLS-compliant framework, but frameworks are encouraged not to rely on such implicit conversions. NOTE It might seem reasonable to provide a volatile constraint on a location that would require that the value stored in the location not be cached between accesses. Instead, CIL includes a volatile. prefix to certain instructions to specify that the value neither be cached nor computed using an existing cache. Such a constraint may be encoded using a custom attribute (see Partition I, section 9.7), although this standard does not specify such an attribute.
8.6.1.3 Local SignaturesA local signature specifies the contract on a local variable allocated during the running of a method. A local signature contains a full location signature, plus it may specify one additional constraint: The byref constraint states that the content of the corresponding location is a managed pointer. A managed pointer may point to a local variable, parameter, field of a compound type, or element of an array. However, when a call crosses a remoting boundary (see Partition I, section 12.5) a conforming implementation may use a copy-in/copy-out mechanism instead of a managed pointer. Thus programs shall not rely on the aliasing behavior of true pointers. In addition, there is one special local signature. The typed reference local variable signature states that the local will contain both a managed pointer to a location and a runtime representation of the type that may be stored at that location. A typed reference signature is similar to a byref constraint, but while the byref specifies the type as part of the byref constraint (and hence as part of the type description), a typed reference provides the type information dynamically. A typed reference is a full signature in itself and cannot be combined with other constraints. In particular, it is not possible to specify a byref whose type is typed reference. The typed reference signature is actually represented as a built-in value type, like the integer and floating point types. In the Base Class Library (see .NET Framework Standard Library Annotated Reference) the type is known as System.TypedReference, and in the assembly language used in Partition II it is designated by the keyword typedref. This type shall only be used for parameters and local variables. It shall not be boxed, nor shall it be used as the type of a field, element of an array, return value, etc.
CLS Rule 14: Typed references are not CLS-compliant. NOTE CLS (consumer): There is no need to accept this type. CLS (extender): There is no need to provide syntax to define this type or to extend interfaces or classes that use this type. CLS (framework): This type shall not appear in exposed members. 8.6.1.4 Parameter SignaturesParameter signatures define constraints on how an individual value is passed as part of a method invocation. Parameter signatures are declared by method definitions. Any valid local signature is a valid parameter signature. 8.6.1.5 Method SignaturesMethod signatures are composed of
Method signatures are declared by method definitions. Only one constraint can be added to a method signature in addition to those of parameter signatures:
Method signatures are used in two different ways. They are used as part of a method definition and as a description of a calling site when calling through a function pointer. In this latter case, the method signature indicates
When used as part of a method definition, the varargs constraint is represented by the choice of calling convention.
CLS Rule 15: The varargs constraint is not part of the CLS, and the only calling convention supported by the CLS is the standard managed calling convention. NOTE CLS (consumer): There is no need to accept methods with variable[-length] argument lists or unmanaged calling conventions. CLS (extender): There is no need to provide syntax to declare varargs methods or unmanaged calling conventions. CLS (framework): Neither varargs methods nor methods with unmanaged calling conventions may be exposed externally. 8.7 Assignment CompatibilityThe constraints in the type signature and the location signature affect assignment compatibility of a value to a location. Assignment compatibility of a value (described by a type signature) to a location (described by a location signature) is defined as follows: One of the types supported by the exact type of the value is the same as the type in the location signature. This allows, for example, an instance of a class that inherits from a base class (hence supports the base class's type contract) to be stored into a location whose type is that of the base class. 8.8 Type Safety and VerificationSince types specify contracts, it is important to know whether a given implementation lives up to these contracts. An implementation that lives up to the enforceable part of the contract (the named signatures) is said to be typesafe. An important part of the contract deals with restrictions on the visibility and accessibility of named items as well as the mapping of names to implementations and locations in memory.
Typesafe implementations only store values described by a type signature in a location that is assignment compatible with the location signature of the location (see Partiton I, section 8.6.1). Typesafe implementations never apply an operation to a value that is not defined by the exact type of the value. Typesafe implementations only access locations that are both visible and accessible to them. In a typesafe implementation, the exact type of a value cannot change. Verification is a mechanical process of examining an implementation and asserting that it is typesafe. Verification is said to succeed if the process proves that an implementation is typesafe. Verification is said to fail if that process does not prove the type safety of an implementation. Verification is necessarily conservative: it may report failure for a typesafe implementation, but it never reports success for an implementation that is not typesafe. For example, most verification processes report implementations that do pointer-based arithmetic as failing verification, even if the implementation is in fact typesafe. There are many different processes that can be the basis of verification. The simplest possible process simply says that all implementations are not typesafe. While correct and efficient, this is clearly not particularly useful. By spending more resources (time and space), a process can correctly identify more typesafe implementations. It has been proven, however, that no mechanical process can in finite time and with no errors correctly identify all implementations as either typesafe or not typesafe. The choice of a particular verification process is thus a matter of engineering, based on the resources available to make the decision and the importance of detecting the type safety of different programming constructs. 8.9 Type DefinersType definers construct a new type from existing types. Implicit types (e.g., built-in types, arrays, and pointers, including function pointers) are defined when they are used. The mention of an implicit type in a signature is in and of itself a complete definition of the type. Implicit types allow the VES to manufacture instances with a standard set of members, interfaces, etc. Implicit types need not have user-supplied names. All other types shall be explicitly defined using an explicit type definition. The explicit type definers are:
NOTE While class definitions always define class types, not all class types require a class definition. Array types and pointer types, which are implicitly defined, are also class types. See Partition I, section 8.2.3. Similarly, not all types defined by a class definition are object types. Array types, explicitly defined object types, and boxed types are object types. Pointer types, function pointer types, and value types are not object types. See Partition I, section 8.2.3. 8.9.1 Array TypesAn array type shall be defined by specifying the element type of the array, the rank (number of dimensions) of the array, and the upper and lower bounds of each dimension of the array. Hence, no separate definition of the array type is needed. The bounds (as well as indices into the array) shall be signed integers. While the actual bounds for each dimension are known at runtime, the signature may specify the information that is known at compile time: no bounds, a lower bound, or both an upper and lower bound. Array elements shall be laid out within the array object in row-major order; i.e., the elements associated with the rightmost array dimension shall be laid out contiguously from lowest to highest index. The actual storage allocated for each array element may include platform-specific padding. Values of an array type are objects; hence an array type is a kind of object type (see Partition I, section 8.2.3). Array objects are defined by the CTS to be a repetition of locations where values of the array element type are stored. The number of repeated values is determined by the rank and bounds of the array. Only type signatures, not location signatures, are allowed as array element types. Exact array types are created automatically by the VES when they are required. Hence, the operations on an array type are defined by the CTS. These generally are: allocating the array based on size and lower bound information, indexing the array to read and write a value, computing the address of an element of the array (a managed pointer), and querying for the rank, bounds, and the total number of values stored in the array.
CLS Rule 16: Arrays shall have elements with a CLS-compliant type, and all dimensions of the array shall have lower bounds of zero. Only the fact that an item is an array and the element type of the array shall be required to distinguish between overloads. When overloading is based on two or more array types, the element types shall be named types. NOTE So-called "jagged arrays" are CLS-compliant, but when overloading multiple array types they are one-dimensional, zero-based arrays of type System.Array. CLS (consumer): There is no need to support arrays of non-CLS types, even when dealing with instances of System.Array. Overload resolution need not be aware of the full complexity of array types. Programmers should have access to the Get, Set, and Address methods on instances of System.Array if there is no language syntax for the full range of array types. CLS (extender): There is no need to provide syntax to define non-CLS types of arrays or to extend interfaces or classes that use non-CLS array types. Shall provide access to the type System.Array, but may assume that all instances will have a CLS-compliant type. While the full array signature must be used to override an inherited method that has an array parameter, the full complexity of array types need not be made visible to programmers. Programmers should have access to the Get, Set, and Address methods on instances of System.Array if there is no language syntax for the full range of array types. CLS (framework): Non-CLS array types shall not appear in exposed members. Where possible, use only one-dimensional, zero-based arrays (vectors) of simple named types, since these are supported in the widest range of programming languages. Overloading on array types should be avoided, and when used shall obey the restrictions.
Array types form a hierarchy, with all array types inheriting from the type System.Array. This is an abstract class (see Partition I, section 8.9.6.2) that represents all arrays regardless of the type of their elements, their rank, or their upper and lower bounds. The VES creates one array type for each distinguishable array type. In general, array types are only distinguished by the type of their elements and their rank. The VES, however, treats single-dimensional, zero-based arrays (also known as vectors) specially. Vectors are also distinguished by the type of their elements, but a vector is distinct from a single-dimensional array of the same element type that has a non-zero lower bound. Zero-dimensional arrays are not supported.
Consider the examples in Table 2-2, using the syntax of CIL as described in Partition II:
8.9.2 Unmanaged Pointer TypesAn unmanaged pointer type (also known simply as a "pointer type") is defined by specifying a location signature for the location the pointer references. Any signature of a pointer type includes this location signature. Hence, no separate definition of the pointer type is needed. While pointer types are Reference Types, values of a pointer type are not objects (see Partition I, section 8.2.3), and hence it is not possible, given a value of a pointer type, to determine its exact type. The CTS provides two typesafe operations on pointer types: one to load the value from the location referenced by the pointer and the other to store an assignment compatible value into that location. The CTS also provides three operations on pointer types (byte-based address arithmetic): adding and subtracting integers from pointers, and subtracting one pointer from another. The results of the first two operations are pointers to the same type signature as the original pointer. See Partition III for details.
CLS Rule 17: Unmanaged pointer types are not CLS-compliant. NOTE CLS (consumer): There is no need to support unmanaged pointer types. CLS (extender): There is no need to provide syntax to define or access unmanaged pointer types. CLS (framework): Unmanaged pointer types shall not be externally exposed.
8.9.3 DelegatesDelegates are the object-oriented equivalent of function pointers. Unlike function pointers, delegates are object-oriented, typesafe, and secure. Delegates are created by defining a class that derives from the base type System.Delegate (see the .NET Framework Standard Library Annotated Reference). Each delegate type shall provide a method named Invoke with appropriate parameters, and each instance of a delegate forwards calls to its Invoke method to a compatible static or instance method on a particular object. The object and method to which it delegates are chosen when the delegate instance is created. In addition to an instance constructor and an Invoke method, delegates may optionally have two additional methods: BeginInvoke and EndInvoke. These are used for asynchronous calls. While, for the most part, delegates appear to be simply another kind of user-defined class, they are tightly controlled. The implementations of the methods are provided by the VES, not user code. The only additional members that may be defined on delegate types are static or instance methods.
8.9.4 Interface Type DefinitionAn interface definition defines an interface type. An interface type is a named group of methods, locations, and other contracts that shall be implemented by any object type that supports the interface contract of the same name. An interface definition is always an incomplete description of a value, and as such can never define a class type or an exact type, nor can it be an object type. Zero or more object types can support an interface type, and only object types can support an interface type. An interface type may require that objects that support it shall also support other (specified) interface types. An object type that supports the named interface contract shall provide a complete implementation of the methods, locations, and other contracts specified (but not implemented by) the interface type. Hence, a value of an object type is also a value of all of the interface types the object type supports. Support for an interface contract is declared, never inferred; i.e., the existence of implementations of the methods, locations, and other contracts required by the interface type does not imply support of the interface contract.
CLS Rule 18: CLS-compliant interfaces shall not require the definition of non-CLS-compliant methods in order to implement them. NOTE CLS (consumer): There is no need to deal with such interfaces. CLS (extender): Need not provide a mechanism for defining such interfaces.. CLS (framework): Shall not expose any non-CLS-compliant methods on interfaces it defines for external use. Interfaces types are necessarily incomplete, since they say nothing about the representation of the values of the interface type. For this reason, an interface type definition shall not provide field definitions for values of the interface type (i.e., instance fields), although it may declare static fields (see Partition I, section 8.4.3). Similarly, an interface type definition shall not provide implementations for any methods on the values of its type. However, an interface type definition may and usually does define method contracts (method name and method signature) that shall be implemented by supporting types. An interface type definition may define and implement static methods (see Partition I, section 8.4.3), since static methods are associated with the interface type itself rather than with any value of the type. Interfaces may have static or virtual methods, but shall not have instance methods.
CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields. NOTE CLS-compliant interfaces may define properties, events, and virtual methods. CLS (consumer): Need not accept interfaces that violate these rules. CLS (extender): Need not provide syntax to author interfaces that violate these rules. CLS (framework): Shall not externally expose interfaces that violate these rules. Where static methods, instance methods, or fields are required, a separate class may be defined that provides them. Interface types may also define event and property contracts that shall be implemented by object types that support the interface. Since event and property contracts reduce to sets of method contracts (see Partition I, section 8.6), the above rules for method definitions apply. For more information, see Partition I, sections 8.11.4 and 8.11.3. Interface type definitions may specify other interface contracts that implementations of the interface type are required to support. See Partition I, section 8.9.8.3 for specifics. An interface type is given a visibility attribute, as described in Partition I, section 8.5.3, that controls from where the interface type may be referenced. An interface type definition is separate from any object type definition that supports the interface type. Hence, it is possible, and often desirable, to have a different visibility for the interface type and the implementing object type. However, since accessibility attributes are relative to the implementing type rather than the interface itself, all members of an interface shall have public accessibility, and no security permissions may be attached to members or to the interface itself. 8.9.5 Class Type DefinitionAll types other than interfaces, and those types for which a definition is automatically supplied by the CTS, are defined by class definitions. A class type is a complete specification of the representation of the values of the class type and all of the contracts (class, interface, method, property, and event) that are supported by the class type. Hence, a class type is an exact type. A class definition, unless it specifies that the class is an abstract object type, not only defines the class type; it also provides implementations for all of the contracts supported by the class type. A class definition, and hence the implementation of the class type, always resides in some assembly. An assembly is a configured set of loadable code modules and other resources that together implement a unit of functionality. NOTE While class definitions always define class types, not all class types require a class definition. Array types and pointer types, which are implicitly defined, are also class types. See Partition I, section 8.2.3. An explicit class definition is used to define:
An explicit class definition:
The semantics of when, and what triggers execution of such type initialization methods, is as follows:
NOTE BeforeFieldInit behavior is intended for initialization code with no interesting side-effects, where exact timing does not matter. Also, under BeforeFieldInit semantics, type initializers are allowed to be executed at or before first access to any static field of that Type at the discretion of the CLI. If a language wishes to provide more rigid behavior e.g., type initialization automatically triggers execution of parent initializers, in a top-to-bottom order, then it can do so by either:
8.9.6 Object Type DefinitionsAll objects are instances of an object type. The object type of an object is set when the object is created, and it is immutable. The object type describes the physical structure of the instance and the operations that are allowed on it. All instances of the same object type have the same structure and the same allowable operations. Object types are explicitly declared by a class type definition, with the exception of Array types, which are intrinsically provided by the VES. 8.9.6.1 Scope and VisibilitySince object type definitions are class type definitions, object type definitions implicitly specify the scope of the name of [the] object type to be the assembly that contains the object type definition, see Partition I, section 8.5.2. Similarly, object type definitions shall also explicitly state the visibility attribute of the object type (either public or assembly); see Partition I, section 8.5.3. 8.9.6.2 ConcretenessAn object type may be marked as abstract by the object type definition. An object type that is not marked abstract is by definition concrete. Only object types may be declared as abstract. Only an abstract object type is allowed to define method contracts for which the type or the VES does not also provide the implementation. Such method contracts are called abstract methods (see Partition I, section 8.11). All methods on an abstract class need not be abstract. It is an error to attempt to create an instance of an abstract object type, whether or not the type has abstract methods. An object type that derives from an abstract object type may be concrete if it provides implementations for any abstract methods in the base object type and is not itself marked as abstract. Instances may be made of such a concrete derived class. Locations may have an abstract type, and instances of a concrete type that derives from the abstract type may be stored in them.
8.9.6.3 Type MembersObject type definitions include member definitions for all of the members of the type. Briefly, members of a type include fields into which values are stored, methods that may be invoked, properties that are available, and events that may be raised. Each member of a type may have attributes as described in Partition I, section 8.4.
8.9.6.4 Supporting Interface ContractsObject type definitions may declare that they support zero or more interface contracts. Declaring support for an interface contract places a requirement on the implementation of the object type to fully implement that interface contract. Implementing an interface contract always reduces to implementing the required set of methods i.e., the methods required by the interface type. The different types that the object type implements i.e., the object type and any implemented interface types are each a separate logical grouping of named members. If a class Foo implements an interface IFoo and IFoo declares a member method int a() and the class also declares a member method int a(), there are two members, one in the IFoo interface type and one in the Foo class type. An implementation of Foo will provide an implementation for both, potentially shared. Similarly, if a class implements two interfaces, IFoo and IBar, each of which defines a method int a(), the class will supply two method implementations, one for each interface, although they may share the actual code of the implementation.
CLS Rule 20: CLS-compliant classes, value types, and interfaces shall not require the implementation of non-CLS-compliant interfaces. NOTE CLS (consumer): Need not accept classes, value types or interfaces that violate this rule. CLS (extender): Need not provide syntax to author classes, value types, or interfaces that violate this rule. CLS (framework): Shall not externally expose classes, value types, or interfaces that violate this rule. 8.9.6.5 Supporting Class ContractsObject type definitions may declare support for one other class contract. Declaring support for another class contract is synonymous with object type inheritance (see Partition I, section 8.9.8.1).
8.9.6.6 ConstructorsNew values of an object type are created via constructors. Constructors shall be instance methods, defined via a special form of method contract, which defines the method contract as a constructor for a particular object type. The constructors for an object type are part of the object type definition. While the CTS and VES ensure that only a properly defined constructor is used to make new values of an object type, the ultimate correctness of a newly constructed object is dependent on the implementation of the constructor itself. Object types shall define at least one constructor method, but that method need not be public. Creating a new value of an object type by invoking a constructor involves the following steps in order:
Inside the constructor, the object type may do any initialization it chooses (possibly none).
CLS Rule 21: An object constructor shall call some class constructor of its base class before any access occurs to inherited instance data. This does not apply to value types, which need not have constructors. CLS Rule 22: An object constructor shall not be called except as part of the creation of an object, and an object shall not be initialized twice. NOTE CLS (consumer): Shall provide syntax for choosing the constructor to be called when an object is created. CLS (extender): Shall provide syntax for defining constructor methods with different signatures. May issue a compiler error if the constructor does not obey these rules. CLS (framework): May assume that object creation includes a call to one of the constructors, and that no object is initialized twice. System.MemberwiseClone (see the .NET Framework Standard Library Annotated Reference) and deserialization (including object remoting) may not run constructors.
8.9.6.7 FinalizersA class definition that creates an object type may supply an instance method to be called when an instance of the class is no longer accessible. The class System.GC (see the .NET Framework Standard Library Annotated Referencen) provides limited control over the behavior of finalizers through the methods SuppressFinalize and ReRegisterForFinalize. Conforming implementations of the CLI may specify and provide additional mechanisms that affect the behavior of finalizers. A conforming implementation of the CLI shall not automatically call a finalizer twice for the same object unless
RATIONALE Programmers expect that finalizers are run precisely once on any given object unless they take an explicit action to cause the finalizer to be run multiple times. It is legal to define a finalizer for a Value Type. That finalizer, however, will only be run for boxed instances of that Value Type.
NOTE Since programmers may depend on finalizers to be called, the CLI should make every effort to ensure that finalizers are called, before it shuts down, for all objects that have not been exempted from finalization by a call to SuppressFinalize. The implementation should specify any conditions under which this behavior cannot be guaranteed. NOTE Since resources may become exhausted if finalizers are not called expeditiously, the CLI should ensure that finalizers are called soon after the instance becomes inaccessible. While relying on memory pressure to trigger finalization is acceptable, implementers should consider the use of additional metrics. 8.9.7 Value Type DefinitionNot all types defined by a class definition are object types (see Partition I, section 8.2.3); in particular, value types are not object types, but they are defined using a class definition. A class definition for a value type defines both the (unboxed) value type and the associated boxed type (see Partition I, section 8.2.4). The members of the class definition define the representation of both:
8.9.8 Type InheritanceInheritance of types is another way of saying that the derived type guarantees support for all of the type contracts of the base type. In addition, the derived type usually provides additional functionality or specialized behavior. A type inherits from a base type by implementing the type contract of the base type. An interface type inherits from zero or more other interfaces[, although interface inheritance is not the same as class inheritance. For more information, see Partition I, section 8.9.8.3]. Value types do not inherit, although the associated boxed type is an object type and hence inherits from other types. The derived class type shall support all of the supported interface contracts, class contracts, event contracts, method contracts, and property contracts of its base type. In addition, all of the locations defined by the base type are also defined in the derived type. The inheritance rules [when followed] guarantee that code that was compiled to work with a value of a base type will still work when passed a value of the derived type. Because of this, a derived type also inherits the implementations of the base type. The derived type may extend, override, and/or hide these implementations.
8.9.8.1 Object Type InheritanceWith the sole exception of System.Object, which does not inherit from any other object type, all object types shall either explicitly or implicitly declare support for (inherit from) exactly one other object type. The graph of the inherits relation shall form a singly rooted tree with System.Object at the base; i.e., all object types eventually inherit from the type System.Object. An object type declares it shall not be used as a base type (be inherited from) by declaring that it is a sealed type.
CLS Rule 23: System.Object is CLS-compliant. Any other CLS-compliant class shall inherit from a CLS-compliant class. Arrays are object types and as such inherit from other object types. Since array object types are manufactured by the VES, the inheritance of arrays is fixed. See Partition I, section 8.9.1.
8.9.8.2 Value Type InheritanceValue Types, in their unboxed form, do not inherit from any type. Boxed value types shall inherit directly from System.ValueType unless they are enumerations, in which case they shall inherit from System.Enum. Boxed value types shall be sealed. Logically, the boxed type corresponding to a value type
The more restrictive rules specified here allow for more efficient implementation without severely compromising functionality. 8.9.8.3 Interface Type InheritanceInterface types may inherit from multiple interface types; i.e., an interface contract may list other interface contracts that shall also be supported. Any type that implements support for an interface type shall also implement support for all of the inherited interface types. This is different from object type inheritance in two ways.
To highlight the last difference, consider an interface, IFoo, that has a single method. An interface, IBar, which inherits from it is requiring that any object type that supports IBar also support IFoo. It does not say anything about which methods IBar itself will have. 8.10 Member InheritanceOnly object types may inherit implementations, hence only object types may inherit members (see Partition I, section 8.9.8). Interface types, while they do inherit from other interface types, only inherit the requirement to implement method contracts, never fields or method implementations.
8.10.1 Field InheritanceA derived object type inherits all of the non-static fields of its base object type. This allows instances of the derived type to be used wherever instances of the base type are expected (the shapes, or layouts, of the instances will be the same). Static fields are not inherited. Just because a field exists does not mean that it may be read or written. The type visibility, field accessibility, and security attributes of the field definition (see Partition I, section 8.5.3) determine if a field is accessible to the derived object type. 8.10.2 Method InheritanceA derived object type inherits all of the instance and virtual methods of its base object type. It does not inherit constructors or static methods. Just because a method exists does not mean that it may be invoked. It shall be accessible via the typed reference that is being used by the referencing code. The type visibility, method accessibility, and security attributes of the method definition (see Partition I, section 8.5.3) determine if a method is accessible to the derived object type. A derived object type may hide a non-virtual (i.e., static or instance) method of its base type by providing a new method definition with the same name or same name and signature. Either method may still be invoked, subject to method accessibility rules, since the type that contains the method always qualifies a method reference. Virtual methods may be marked as final, in which case they shall not be overridden in a derived object type. This ensures that the implementation of the method is available, by a virtual call, on any object that supports the contract of the base class that supplied the final implementation. If a virtual method is not final, it is possible to demand a security permission in order to override the virtual method, so that the ability to provide an implementation can be limited to classes that have particular permissions. When a derived type overrides a virtual method, it may specify a new accessibility for the virtual method, but the accessibility in the derived class shall permit at least as much access as the access granted to the method it is overriding. See Partition I, section 8.5.3.
8.10.3 Property and Event InheritanceProperties and events are fundamentally constructs of the metadata intended for use by tools that target the CLI and are not directly supported by the VES itself. It is, therefore, the job of the source language compiler and the Reflection Library (see the .NET Framework Standard Library Annotated Reference) to determine rules for name hiding, inheritance, and so forth. The source compiler shall generate CIL that directly accesses the methods named by the events and properties, not the events or properties themselves. 8.10.4 Hiding, Overriding, and LayoutThere are two separate issues involved in inheritance. The first is which contracts a type shall implement and hence which member names and signatures it shall provide. The second is the layout of the instance so that an instance of a derived type can be substituted for an instance of any of its base types. Only the non-static fields and the virtual methods that are part of the derived type affect the layout of an object. The CTS provides independent control over both the names that are visible from a base type (hiding) and the sharing of layout slots in the derived class (overriding). Hiding is controlled by marking a member in the derived class as either hide by name or hide by name-and-signature. Hiding is always performed based on the kind of member; that is, derived field names may hide base field names, but not method names, property names, or event names. If a derived member is marked hide by name, then members of the same kind in the base class with the same name are not visible in the derived class; if the member is marked hide by name-and-signature then only a member of the same kind with exactly the same name and type (for fields) or method signature (for methods) is hidden in the derived class. Implementation of the distinction between these two forms of hiding is provided entirely by source language compilers and the Reflection Library; it has no direct impact on the VES itself. For example: class Base { field int32 A; field System.String A; method int32 A(); method int32 A(int32); } class Derived inherits from Base { field int32 A; hidebysig method int32 A(); }
The member names available in type Derived are listed above in Table 2-3.: While hiding applies to all members of a type, overriding deals with object layout and is applicable only to instance fields and virtual methods. The CTS provides two forms of member overriding, new slot and expect existing slot. A member of a derived type that is marked as a new slot will always get a new slot in the object's layout, guaranteeing that the base field or method is available in the object by using a qualified reference that combines the name of the base type with the name of the member and its type or signature. A member of a derived type that is marked as expect existing slot will reuse (i.e., share or override) a slot that corresponds to a member of the same kind (field or method), name, and type if one already exists from the base type; if no such slot exists, a new slot is allocated and used. The general algorithm that is used for determining the names in a type and the layout of objects of the type is roughly as follows:
8.11 Member DefinitionsObject type definitions, interface type definitions, and value type definitions may include member definitions. Field definitions define the representation of values of the type by specifying the substructure of the value. Method definitions define operations on values of the type and operations on the type itself (static methods). Property and event definitions may only be defined on object types. Property and events define named groups of accessor method definitions that implement the named event or property behavior. Nested type declarations define types whose names are scoped by the enclosing type and whose instances have full access to all members of the enclosing class. Depending on the kind of type definition, there are restrictions on the member definitions allowed. 8.11.1 Method DefinitionsMethod definitions are composed of a name, a method signature, and optionally an implementation of the method. The method signature defines the calling convention, type of the parameters to the method, and the return type of the method (see Partition I, section 8.6.1). The implementation is the code to execute when the method is invoked. A value type or object type may define only one method of a given name and signature. However, a derived object type may have methods that are of the same name and signature as its base object type. See Partition I, sections 8.10.2 and 8.10.4. The name of the method is scoped to the type (see Partition I, section 8.5.2). Methods may be given accessibility attributes (see Partition I, section 8.5.3). Methods may only be invoked with arguments that are assignment compatible with the parameter types of the method signature. The return value of the method shall also be assignment compatible with the location in which it is stored. Methods may be marked as static, indicating that the method is not an operation on values of the type but rather an operation associated with the type as a whole. Methods not marked as static define the valid operations on a value of a type. When a non-static method is invoked, a particular value of the type, referred to as this or the this pointer, is passed as an implicit parameter. A method definition that does not include a method implementation shall be marked as abstract. All non-static methods of an interface definition are abstract. Abstract method definitions are only allowed in object types that are marked as abstract. A non-static method definition in an object type may be marked as virtual, indicating that an alternate implementation may be provided in derived types. All non-static method definitions in interface definitions shall be virtual methods. A virtual method may be marked as final, indicating that derived object types are not allowed to override the method implementation.
8.11.2 Field DefinitionsField definitions are composed of a name and a location signature. The location signature defines the type of the field and the accessing constraints (see Partition I, section 8.6.1). A value type or object type may define only one field of a given name and type. However, a derived object type may have fields that are of the same name and type as its base object type. See Partition I, sections 8.10.1 and 8.10.4. The name of the field is scoped to the type (see Partition I, section 8.5.2). Fields may be given accessibility attributes (see Partition I, section 8.5.3). Fields may only store values that are assignment compatible with the type of the field (see Partition I, section 8.3.1). Fields may be marked as static, indicating that the field is not part of values of the type but rather a location associated with the type as a whole. Locations for the static fields are created when the type is loaded and initialized when the type is initialized. Fields not marked as static define the representation of a value of a type by defining the substructure of the value (see Partition I, section 8.4.1). Locations for such fields are created within every value of the type whenever a new value is constructed. They are initialized during construction of the new value. A non-static field of a given name is always located at the same place within every value of the type.
A field that is marked serializable is to be serialized as part of the persistent state of a value of the type. This standard does not specify the mechanism by which this is accomplished.
8.11.3 Property DefinitionsA property definition defines a named value and the methods that access the value. A property definition defines the accessing contracts on that value. Hence, the property definition specifies which accessing methods exist and their respective method contracts. An implementation of a type that declares support for a property contract shall implement the accessing methods required by the property contract. The implementation of the accessing methods defines how the value is retrieved and stored. A property definition is always part of either an interface definition or a class definition. The name and value of a property definition is scoped to the object type or the interface type that includes the property definition. While all of the attributes of a member may be applied to a property (accessibility, static, etc.), these are not enforced by the CTS. Instead, the CTS requires that the method contracts that comprise the property shall match the method implementations, as with any other method contract. There are no CIL instructions associated with properties, just metadata. By convention, properties define a getter method (for accessing the current value of the property) and optionally a setter method (for modifying the current value of the property). The CTS places no restrictions on the set of methods associated with a property, their names, or their usage.
CLS Rule 24: The methods that implement the getter and setter methods of a property shall be marked SpecialName in the metadata. CLS Rule 25: The accessibility of a property and of its accessors shall be identical. CLS Rule 26: A property and its accessors shall all be static, all be virtual, or all be instance. CLS Rule 27: The type of a property shall be the return type of the getter and the type of the last argument of the setter. The types of the parameters of the property shall be the types of the parameters to the getter and the types of all but the final parameter of the setter. All of these types shall be CLS-compliant, and shall not be managed pointers (i.e., shall not be passed by reference). CLS Rule 28: Properties shall adhere to a specific naming pattern. See Partition I, section 10.4. The SpecialName attribute referred to in CLS rule 24 shall be ignored in appropriate name comparisons and shall adhere to identifier rules. NOTE CLS (consumer): Shall ignore the SpecialName bit in appropriate name comparisons and shall adhere to identifier rules. Otherwise, no direct support other than the usual access to the methods that define the property. CLS (extender): Shall ignore the SpecialName bit in appropriate name comparisons and shall adhere to identifier rules. Otherwise, no direct support other than the usual access to the methods that define the property. In particular, an extender need not be able to define properties. CLS (framework): Shall design understanding that not all CLS languages will access the property using special syntax. 8.11.4 Event DefinitionsThe CTS supports events in precisely the same way that it supports properties (see Partition I, section 8.11.3). The conventional methods, however, are different and include means for subscribing and unsubscribing to events as well as for firing the event.
CLS Rule 29: The methods that implement an event shall be marked SpecialName in the metadata. CLS Rule 30: The accessibility of an event and of its accessors shall be identical. CLS Rule 31: The add and remove methods for an event shall both either be present or absent. CLS Rule 32: The add and remove methods for an event shall each take one parameter whose type defines the type of the event and that shall be derived from System.Delegate. CLS Rule 33: Events shall adhere to a specific naming pattern. See Partition I, section 10.4. The SpecialName attribute referred to in CLS rule 29 shall be ignored in appropriate name comparisons and shall adhere to identifier rules. NOTE CLS (consumer): Shall ignore the SpecialName bit in appropriate name comparisons and shall adhere to identifier rules. Otherwise, no direct support other than the usual access to the methods that define the event. CLS (extender): Shall ignore the SpecialName bit in appropriate name comparisons and shall adhere to identifier rules. Otherwise, no direct support other than the usual access to the methods that define the event. In particular, an extender need not be able to define events. CLS (framework): Shall design based on the understanding that not all CLS languages will access the event using special syntax. 8.11.5 Nested Type DefinitionsA nested type definition is identical to a top-level type definition, with one exception: a top-level type has a visibility attribute, while the visibility of a nested type is the same as the visibility of the enclosing type. See Partition I, section 8.5.3.
|