10 Name and Type Rules for the Common Language Specification


ANNOTATION

This section provides the context and detailed information for a number of CLS rules, that generally is not contained elsewhere, as well as CLS rules 37 41 and their context. The following major section (section 11, Collected CLS Rules) is just a list of the CLS rules listed earlier in the standard, without their context, and is intended as a reference only.


10.1 Identifiers

Languages that are either case-sensitive or case-insensitive can support the CLS. Since its rules apply only to items exposed to other languages, private members or types that aren't exported from an assembly may use any names they choose. For interoperation, however, there are some restrictions.

In order to make tools work well with a case-sensitive language, it is important that the exact case of identifiers be maintained. At the same time, when dealing with non-English languages encoded in Unicode, there may be more than one way to represent precisely the same identifier that includes combining characters. The CLS requires that identifiers obey the restrictions of the appropriate Unicode standard and persist them in Canonical form C, which preserves case but forces combining characters into a standard representation. See CLS Rule 4, in Partition I, section 8.5.1.

At the same time, it is important that externally visible names not conflict with one another when used from a case-insensitive programming language. As a result, all identifier comparisons shall be done internally to CLS-compliant tools using the Canonical form KC, which first transforms characters to their case-canonical representation. See CLS Rule 4, in Partition I, section 8.5.1.

When a compiler for a CLS-compliant language supports interoperability with a non-CLS-compliant language, it must be aware that the CTS and VES perform all comparisons using code-point (i.e., byte-by-byte) comparison. Thus, even though the CLS requires that persisted identifiers be in Canonical form C, references to non-CLS identifiers will have to be persisted using whatever encoding the non-CLS language chose to use. It is a language design issue, not covered by the CTS or the CLS, precisely how this should be handled.

ANNOTATION

For more information, see the annotation in Partition I, section 8.5.1.


10.2 Overloading

NOTE

The CTS, while it describes inheritance, object layout, name hiding, and overriding of virtual methods, does not discuss overloading at all. While this is surprising, it arises from the fact that overloading is entirely handled by compilers that target the CTS and not the type system itself. In the metadata, all references to types and type members are fully resolved and include the precise signature that is intended. This choice was made since every programming language has its own set of rules for coercing types and the VES does not provide a means for expressing those rules.


Following the rules of the CTS, it is possible for duplicate names to be defined in the same scope as long as they differ in either kind (field, method, etc.) or signature. The CLS imposes a stronger restriction for overloading methods. Within a single scope, a given name may refer to any number of methods provided they differ in any of the following:

  • Number of parameters

  • Type of each argument

Notice that the signature includes more information but CLS-compliant languages need not produce or consume classes that differ only by that additional information (see Partition II for the complete list of information carried in a signature):

  • Calling convention

  • Custom modifiers

  • Return type

  • Whether a parameter is passed by value, or whether it is passed by reference (i.e., as a managed pointer or by-ref)

There is one exception to this rule. For the special names op_Implicit and op_Explicit described in Partition I, section 10.3.3 methods may be provided that differ only by their return type. These are marked specially and may be ignored by compilers that don't support operator overloading.

Properties shall not be overloaded by type (that is, by the return type of their getter method), but they may be overloaded with different numbers or types of indices (that is, by the number and types of the parameters of its getter method). The overloading rules for properties are identical to the method overloading rules.

CLS Rule 37: Only properties and methods may be overloaded.

CLS Rule 38: Properties, instance methods, and virtual methods may be overloaded based only on the number and types of their parameters, except the conversion operators named op_Implicit and op_Explicit, which may also be overloaded based on their return type.

NOTE

CLS (consumer): May assume that only properties and methods are overloaded, and need not support overloading based on return type unless providing special syntax for operator overloading. If return type overloading isn't supported, then the op_Implicit and op_Explicit may be ignored, since the functionality shall be provided in some other way by a CLS-compliant framework.

CLS (extender): Should not permit the authoring of overloads other than those specified here. It is not necessary to support operator overloading at all, hence it is possible to entirely avoid support for overloading on return type.

CLS (framework): Shall not publicly expose overloading except as specified here. Frameworks authors should bear in mind that many programming languages, including Object-Oriented languages, do not support overloading and will expose overloaded methods or properties through mangled names. Most languages support neither operator overloading nor overloading based on return type, so op_Implicit and op_Explicit shall always be augmented with some alternative way to gain the same functionality.


ANNOTATION

Not all languages support overloading. A strong opponent is Eiffel. Emmanuel Stapf, a member of the ECMA technical committee responsible for this standard and a senior software engineer for Eiffel, has contributed the following argument for why overloading should not have been part of the standard:

Those who believe that object-oriented languages should not support overloading have adopted the principle that different things should have different names. The key, they believe, to simplicity is a one-to-one mapping. To keep its specification as simple as possible, Eiffel does not support overloading.

An example of how overloading can cause problems is taken from the Point class. You can represent a Point instance with either polar coordinates or Cartesian coordinates. The values for both kinds of coordinates are real numbers. Suppose you then use a simple constructor:

 
 .ctor (Single, Single) 

With this constructor, you can create instances of Point in only one of the coordinate systems.

Inheritance and Overloading

Overloading also does not work well with inheritance. A simple example illustrates this.

Class A has the method void f(X x).

Class B, inheriting from A, provides a new implementation for f.

Class Y inherits from class X.

 
 A a; B b; X x; Y y; a = b; x = y; a.f(x); a.f(y); b.f(x); b.f(y); 

In this example there is not much ambiguity. But suppose, in B, that you add the following new overloaded definition of f:

 
 void f (Y y); 

Doing so breaks the previous code. Before the new overloaded definition, all calls resolved in calling the version of void f(X x) defined in B. Now some calls resolve into void f(X x) and some into void f(Y y). It is not clear which ones resolve to which.

Variance and Overloading

Although the CLI supports only no-variant redefinition of routines, some other languages support either covariant redefinition or contravariant redefinition. Overloading works well with no-variant definition but does not work well with covariant or contravariant languages. Following is an example of a covariant case:

Class A has the method void f(X x).

Class B has void f(Y y).

Class Y inherits from class X.

Suppose that in class B you want to covariantly redefine void f(X x) to be void f(Y y). If you allow overloading you cannot do that, because you will end up with two routines with the same signature. This is not allowed. Therefore you need to rename the void f(Y y) initially present in B to something else, removing overloading altogether.

Eiffel's argument comes down to three ideas about variant redefinition:

  1. Not all languages support it.

  2. It has inherent limitations.

  3. Its lack of one-to-one mapping results in a lack of simplicity in the programming model.

These arguments are well expressed in an article by Bertrand Meyer, published in the October/November 2001 issue of the Journal of Object-Oriented Programming: http://www.inf.ethz.ch/personal/meyer/publications/joop/overloading.pdf.

Emmanuel Stapf


10.3 Operator Overloading

CLS-compliant consumer and extender tools are under no obligation to allow defining of operator overloading. CLS-compliant consumer and extender tools do not have to provide a special mechanism to call these methods.

NOTE

This topic is addressed by the CLS so that

  • languages that do provide operator overloading can describe their rules in a way that other languages can understand, and

  • languages that do not provide operator overloading can still access the underlying functionality without the addition of special syntax.


Operator overloading is described by using the names specified below, and by setting a special bit in the metadata (SpecialName) so that they do not collide with the user's namespace. A CLS-compliant producer tool shall provide some means for setting this bit. If these names are used, they shall have precisely the semantics described here.

ANNOTATION

From the point of view of language design, operator overloading is a very interesting piece of the design space. Adding operator overloading gives a great deal of convenience to the programmer. At the same time, it is the single greatest source of confusion in most of the programming languages that have it. When you are dealing with someone else's code, where an operator like "+" has been redefined but you are not aware of it, you encounter cases in which it no longer means what you intuitively understand.

In designing the CLI, however, the decision was made not to restrict operator overloading. Because most languages do not include operator overloading, these languages had to agree to provide another way to do whatever would have been done through operator overloading. To do that, a standardized set of operator overloads are given names. If you're using a language that does not have operator overloading, you can use those names to get the same behavior. In languages that overload operators, these names are not used. In languages that do not overload operators, standard operators are not customizable or extendable, but other functionality is available with the names in Table 2-4, which is a very large list, containing operations from multiple languages.

Adding the conversion operators was very contentious because it required the inclusion of the odd codicil to CLS rule 38. Rule 38 says:

Properties, instance methods, and virtual methods may be overloaded based only on the number and types of their parameters, except the conversion operators named op_Implicit and op_Explicit, which may also be overloaded based on their return type.

Everyone agreed unanimously that overloading the return type is a bad idea, but in this case there was no alternative to getting the needed functionality.


10.3.1 Unary Operators

Unary operators take one argument, perform some operation on it, and return the result. They are represented as static methods on the class that defines the type of their one operand or their return type. Table 2-4, Unary Operator Names, shows the names that are defined.

10.3.2 Binary Operators

Binary operators take two arguments, perform some operation, and return a value. They are represented as static methods on the class that defines the type of one of their two operands or the return type. Table 2-5, Binary Operator Names, shows the names that are defined.

10.3.3 Conversion Operators

Conversion operators are unary operations that allow conversion from one type to another. The operator method shall be defined as a static method on either the operand or return type. There are two types of conversions:

  • An implicit (widening) coercion shall not lose any magnitude or precision. These should be provided using a method named op_Implicit.

  • An explicit (narrowing) coercion may lose magnitude or precision. These should be provided using a method named op_Explicit.

Table 2-4. Unary Operator Names

Name

ISO/IEC 14882:1998 C++ Operator Symbol

op_Decrement

Similar to --[1]

op_Increment

Similar to ++[1]

op_UnaryNegation

- (unary)

op_UnaryPlus

+ (unary)

op_LogicalNot

!

op_True[2]

Not defined

op_False[2]

Not defined

op_AddressOf

& (unary)

op_OnesComplement

~

op_PointerDereference

* (unary)

[1] From a pure C++ point of view, the way one must write these functions for the CLI differs in one very important aspect. In C++, these methods must increment or decrement their operand directly, whereas, in CLI, they must not; instead, they simply return the value of their operand +/- 1, as appropriate, without modifying their operand. The operand must be incremented or decremented by the compiler that generates the code for the ++/-- operator, separate from the call to these methods.

[2] The op_True and op_False operators do not exist in C++. They are provided to support tri-state Boolean types, such as those used in database languages.

Table 2-5. Binary Operator Names

Name

C++ Operator Symbol

op_Addition

+ (binary)

op_Subtraction

- (binary)

op_Multiply

* (binary)

op_Division

/

op_Modulus

%

op_ExclusiveOr

^

op_BitwiseAnd

& (binary)

op_BitwiseOr

|

op_LogicalAnd

&&

op_LogicalOr

||

op_Assign

=

op_LeftShift

<<

op_RightShift

>>

op_SignedRightShift

Not defined

op_UnsignedRightShift

Not defined

op_Equality

==

op_GreaterThan

>

op_LessThan

<

op_Inequality

!=

op_GreaterThanOrEqual

 

op_LessThanOrEqual

<=

op_UnsignedRightShiftAssignment

Not defined

op_MemberSelection

->

op_RightShiftAssignment

>>=

op_MultiplicationAssignment

*=

op_PointerToMemberSelection

->*

op_SubtractionAssignment

-=

op_ExclusiveOrAssignment

^=

op_LeftShiftAssignment

<<=

op_ModulusAssignment

%=

op_AdditionAssignment

+=

op_BitwiseAndAssignment

&=

op_BitwiseOrAssignment

|=

op_Comma

,

op_DivisionAssignment

/=

NOTE

Conversions provide functionality that can't be generated in other ways, and many languages will not support the use of the conversion operators through special syntax. Therefore, CLS rules require that the same functionality be made available through an alternate mechanism. Using the more common ToXxx (where Xxx is the target type) and FromYyy (where Yyy is the name of the source type) naming pattern is recommended.


Because these operations may exist on the class of their operand type (so-called "from" conversions) and would therefore differ on their return type only, the CLS specifically allows that these two operators be overloaded based on their return type. The CLS, however, also requires that if this form of overloading is used then the language shall provide an alternate means for providing the same functionality since not all CLS languages will implement operators with special syntax.

CLS Rule 39: If either op_Implicit or op_Explicit is provided, an alternate means of providing the coercion shall be provided.

NOTE

CLS (consumer): Where appropriate to the language design, use the existence of op_Implicit and/or op_Explicit in choosing method overloads and generating automatic coercions.

CLS (extender): Where appropriate to the language design, implement user-defined implicit or explicit coercion operators using the corresponding op_Implicit, op_Explicit, ToXxx, and/or FromXxx methods.

CLS (framework): If coercion operations are supported, they shall be provided as FromXxx and ToXxx, and optionally op_Implicit and op_Explicit as well. CLS frameworks are encouraged to provide such coercion operations.


ANNOTATION

The most contentious discussion during the development of the standard on overloading operators focused on conversion operators, because it required the inclusion of the odd codicil to CLS rule 38. No one was happy with it because although all agreed that overloading should not be allowed based on the return type of a method, it ended up being allowed in this one case. There was no other way to get the needed functionality.


10.4 Naming Patterns

See also Partition V[, Annex D.1, Naming Guidelines].

While the CTS does not dictate the naming of properties or events, the CLS does specify a pattern to be observed.

For Events:

An individual event is created by choosing or defining a delegate type that is used to signal the event. Then, three methods are created with names based on the name of the event and with a fixed signature. For the examples below we define an event named Click that uses a delegate type named EventHandler.

 
 EventAdd, used to add a handler for an event         Pattern: void add_<EventName> (<DelegateType> handler)         Example: void add_Click (EventHandler handler); EventRemove, used to remove a handler for an event         Pattern: void remove_<EventName> (<DelegateType> handler)         Example: void remove_Click (EventHandler handler); EventRaise, used to signal that an event has occurred         Pattern: void family raise_<EventName> (Event e) 

For Properties:

An individual property is created by deciding on the type returned by its getter method and the types of the getter's parameters (if any). Then, two methods are created with names based on the name of the property and these types. For the examples below we define two properties: Name takes no parameters and returns a System.String, while Item takes a System.Object parameter and returns a System.Object. Item is referred to as an indexed property, meaning that it takes parameters and thus may appear to the user as though it were an array with indices.

 
 PropertyGet, used to read the value of the property         Pattern: <PropType> get_<PropName> (<Indices>)         Example: System.String get_Name ();         Example: System.Object get_Item (System.Object key); PropertySet, used to modify the value of the property         Pattern: void set_<PropName> (<Indices>, <PropType>)         Example: void set_Name (System.String name);         Example: void set_Item (System.Object key, System.Object value); 

10.5 Exceptions

The CLI supports an exception handling model, which is introduced in Partition I, section 12.4.2. CLS-compliant frameworks may define and throw externally visible exceptions, but there are restrictions on the types of objects thrown:

CLS Rule 40: Objects that are thrown shall be of type System.Exception or inherit from it. Nonetheless, CLS-compliant methods are not required to block the propagation of other types of exceptions.

NOTE

CLS (consumer): Need not support throwing or catching of objects that are not of the specified type.

CLS (extender): Must support throwing of objects of type System.Exception or a type inheriting from it. Need not support throwing of objects of other types.

CLS (framework): Shall not publicly expose thrown objects that are not of type System.Exception or a type inheriting from it.


10.6 Custom Attributes

In order to allow languages to provide a consistent view of custom attributes across language boundaries, the Base Class Library provides support for the following rules defined by the CLS:

CLS Rule 41: Attributes shall be of type System.Attribute, or inherit from it.

NOTE

CLS (consumer): Need not support attributes that are not of the specified type.

CLS (extender): Must support the authoring of custom attributes.

CLS (framework): Shall not publicly expose attributes that are not of type System.Attribute or a type inheriting from it.


The use of a particular attribute class may be restricted in various ways by placing an attribute on the attribute class. The System.AttributeUsageAttribute is used to specify these restrictions. The restrictions supported by the System.AttributeUsageAttribute are:

  • What kinds of constructs (types, methods, assemblies, etc.) may have the attribute applied to them. By default, instances of an attribute class can be applied to any construct. This is specified by setting the value of the ValidOn property of System.AttributeUsageAttribute. Several constructs may be combined.

  • Multiple instances of the attribute class may be applied to a given piece of metadata. By default, only one instance of any given attribute class can be applied to a single metadata item. The AllowMultiple property of the attribute is used to specify the desired value.

  • Do not inherit the attribute when applied to a type. By default, any attribute attached to a type should be inherited to types that derive from it. If multiple instances of the attribute class are allowed, the inheritance performs a union of the attributes inherited from the parent and those explicitly applied to the child type. If multiple instances are not allowed, then an attribute of that type applied directly to the child overrides the attribute supplied by the parent. This is specified by setting the Inherited property of System.AttributeUsageAttribute to the desired value.

NOTE

Since these are CLS rules and not part of the CTS itself, tools are required to specify explicitly the custom attributes they intend to apply to any given metadata item. That is, compilers or other tools that generate metadata must implement the AllowMultiple and Inherit rules. The CLI does not supply attributes automatically. The usage of attributes in the CLI is further described in Partition II.




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

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