In the preceding lessons, you have seen inheritance mentioned several times. In the Java language, classes can be derived from other classes, thereby inheriting fields and methods from those classes. Show
Definitions: A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class). Excepting Object, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of Object. Classes can be derived from classes that are derived from classes that are derived from classes, and so on, and ultimately derived from the topmost class, Object. Such a class is said to be descended from all the classes in the inheritance chain stretching back to Object. The idea of inheritance is simple but powerful: When you want to create a new class and there is already a class that includes some of the code that you want, you can derive your new class from the existing class. In doing this, you can reuse the fields and methods of the existing class without having to write (and debug!) them yourself. A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass. The Java Platform Class HierarchyThe Object class, defined in the java.lang package, defines and implements behavior common to all classes—including the ones that you write. In the Java platform, many classes derive directly from Object, other classes derive from some of those classes, and so on, forming a hierarchy of classes. All Classes in the Java Platform are Descendants of Object At the top of the hierarchy, Object is the most general of all classes. Classes near the bottom of the hierarchy provide more specialized behavior. An Example of InheritanceHere is the sample code for a possible implementation of a Bicycle class that was presented in the Classes and Objects lesson: public class Bicycle { // the Bicycle class has three fields public int cadence; public int gear; public int speed; // the Bicycle class has one constructor public Bicycle(int startCadence, int startSpeed, int startGear) { gear = startGear; cadence = startCadence; speed = startSpeed; } // the Bicycle class has four methods public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void applyBrake(int decrement) { speed -= decrement; } public void speedUp(int increment) { speed += increment; } } A class declaration for a MountainBike class that is a subclass of Bicycle might look like this: public class MountainBike extends Bicycle { // the MountainBike subclass adds one field public int seatHeight; // the MountainBike subclass has one constructor public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) { super(startCadence, startSpeed, startGear); seatHeight = startHeight; } // the MountainBike subclass adds one method public void setHeight(int newValue) { seatHeight = newValue; } } MountainBike inherits all the fields and methods of Bicycle and adds the field seatHeight and a method to set it. Except for the constructor, it is as if you had written a new MountainBike class entirely from scratch, with four fields and five methods. However, you didn't have to do all the work. This would be especially valuable if the methods in the Bicycle class were complex and had taken substantial time to debug. What You Can Do in a SubclassA subclass inherits all of the public and protected members of its parent, no matter what package the subclass is in. If the subclass is in the same package as its parent, it also inherits the package-private members of the parent. You can use the inherited members as is, replace them, hide them, or supplement them with new members:
The following sections in this lesson will expand on these topics. Private Members in a SuperclassA subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass. A nested class has access to all the private members of its enclosing class—both fields and methods. Therefore, a public or protected nested class inherited by a subclass has indirect access to all of the private members of the superclass. Casting ObjectsWe have seen that an object is of the data type of the class from which it was instantiated. For example, if we write public MountainBike myBike = new MountainBike(); then myBike is of type MountainBike. MountainBike is descended from Bicycle and Object. Therefore, a MountainBike is a Bicycle and is also an Object, and it can be used wherever Bicycle or Object objects are called for. The reverse is not necessarily true: a Bicycle may be a MountainBike, but it isn't necessarily. Similarly, an Object may be a Bicycle or a MountainBike, but it isn't necessarily. Casting shows the use of an object of one type in place of another type, among the objects permitted by inheritance and implementations. For example, if we write Object obj = new MountainBike(); then obj is both an Object and a MountainBike (until such time as obj is assigned another object that is not a MountainBike). This is called implicit casting. If, on the other hand, we write MountainBike myBike = obj; we would get a compile-time error because obj is not known to the compiler to be a MountainBike. However, we can tell the compiler that we promise to assign a MountainBike to obj by explicit casting: MountainBike myBike = (MountainBike)obj; This cast inserts a runtime check that obj is assigned a MountainBike so that the compiler can safely assume that obj is a MountainBike. If obj is not a MountainBike at runtime, an exception will be thrown. Note: You can make a logical test as to the type of a particular object using the instanceof operator. This can save you from a runtime error owing to an improper cast. For example: if (obj instanceof MountainBike) { MountainBike myBike = (MountainBike)obj; } Here the instanceof operator verifies that obj refers to a MountainBike so that we can make the cast with knowledge that there will be no runtime exception thrown.
A class is a data structure that may contain data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, finalizers, and static constructors), and nested types. Class types support inheritance, a mechanism whereby a derived class can extend and specialize a base class. 14.2 Class declarations14.2.1 GeneralA class_declaration is a type_declaration (§13.7) that declares a new class. class_declaration : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list? class_base? type_parameter_constraints_clause* class_body ';'? ;A class_declaration consists of an optional set of attributes (§21), followed by an optional set of class_modifiers (§14.2.2), followed by an optional partial modifier (§14.2.7), followed by the keyword class and an identifier that names the class, followed by an optional type_parameter_list (§14.2.3), followed by an optional class_base specification (§14.2.4), followed by an optional set of type_parameter_constraints_clauses (§14.2.5), followed by a class_body (§14.2.6), optionally followed by a semicolon. A class declaration shall not supply a type_parameter_constraints_clauses unless it also supplies a type_parameter_list. A class declaration that supplies a type_parameter_list is a generic class declaration. Additionally, any class nested inside a generic class declaration or a generic struct declaration is itself a generic class declaration, since type arguments for the containing type shall be supplied to create a constructed type (§8.4). 14.2.2 Class modifiers14.2.2.1 GeneralA class_declaration may optionally include a sequence of class modifiers: class_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'abstract' | 'sealed' | 'static' | unsafe_modifier // unsafe code support ;unsafe_modifier (§22.2) is only available in unsafe code (§22). It is a compile-time error for the same modifier to appear multiple times in a class declaration. The new modifier is permitted on nested classes. It specifies that the class hides an inherited member by the same name, as described in §14.3.5. It is a compile-time error for the new modifier to appear on a class declaration that is not a nested class declaration. The public, protected, internal, and private modifiers control the accessibility of the class. Depending on the context in which the class declaration occurs, some of these modifiers might not be permitted (§7.5.2). When a partial type declaration (§14.2.7) includes an accessibility specification (via the public, protected, internal, and private modifiers), that specification shall agree with all other parts that include an accessibility specification. If no part of a partial type includes an accessibility specification, the type is given the appropriate default accessibility (§7.5.2). The abstract, sealed, and static modifiers are discussed in the following subclauses. 14.2.2.2 Abstract classesThe abstract modifier is used to indicate that a class is incomplete and that it is intended to be used only as a base class. An abstract class differs from a non-abstract class in the following ways:
When a non-abstract class is derived from an abstract class, the non-abstract class shall include actual implementations of all inherited abstract members, thereby overriding those abstract members.
If one or more parts of a partial type declaration (§14.2.7) of a class include the abstract modifier, the class is abstract. Otherwise, the class is non-abstract. 14.2.2.3 Sealed classesThe sealed modifier is used to prevent derivation from a class. A compile-time error occurs if a sealed class is specified as the base class of another class. A sealed class cannot also be an abstract class.
If one or more parts of a partial type declaration (§14.2.7) of a class include the sealed modifier, the class is sealed. Otherwise, the class is unsealed. 14.2.2.4 Static classes14.2.2.4.1 GeneralThe static modifier is used to mark the class being declared as a static class. A static class shall not be instantiated, shall not be used as a type and shall contain only static members. Only a static class can contain declarations of extension methods (§14.6.10). A static class declaration is subject to the following restrictions:
It is a compile-time error to violate any of these restrictions. A static class has no instance constructors. It is not possible to declare an instance constructor in a static class, and no default instance constructor (§14.11.5) is provided for a static class. The members of a static class are not automatically static, and the member declarations shall explicitly include a static modifier (except for constants and nested types). When a class is nested within a static outer class, the nested class is not a static class unless it explicitly includes a static modifier. If one or more parts of a partial type declaration (§14.2.7) of a class include the static modifier, the class is static. Otherwise, the class is not static. 14.2.2.4.2 Referencing static class typesA namespace_or_type_name (§7.8) is permitted to reference a static class if
A primary_expression (§11.7) is permitted to reference a static class if
In any other context, it is a compile-time error to reference a static class.
14.2.3 Type parametersA type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. By constrast, a type argument (§8.4.2) is the type that is substituted for the type parameter when a constructed type is created. type_parameter_list : '<' type_parameters '>' ; type_parameters : attributes? type_parameter | type_parameters ',' attributes? type_parameter ;type_parameter is defined in §8.5. Each type parameter in a class declaration defines a name in the declaration space (§7.3) of that class. Thus, it cannot have the same name as another type parameter of that class or a member declared in that class. A type parameter cannot have the same name as the type itself. Two partial generic type declarations (in the same program) contribute to the same unbound generic type if they have the same fully qualified name (which includes a generic_dimension_specifier (§11.7.16) for the number of type parameters) (§7.8.3). Two such partial type declarations shall specify the same name for each type parameter, in order. 14.2.4 Class base specification14.2.4.1 GeneralA class declaration may include a class_base specification, which defines the direct base class of the class and the interfaces (§17) directly implemented by the class. class_base : ':' class_type | ':' interface_type_list | ':' class_type ',' interface_type_list ; interface_type_list : interface_type (',' interface_type)* ;14.2.4.2 Base classesWhen a class_type is included in the class_base, it specifies the direct base class of the class being declared. If a non-partial class declaration has no class_base, or if the class_base lists only interface types, the direct base class is assumed to be object. When a partial class declaration includes a base class specification, that base class specification shall reference the same type as all other parts of that partial type that include a base class specification. If no part of a partial class includes a base class specification, the base class is object. A class inherits members from its direct base class, as described in §14.3.4.
For a constructed class type, including a nested type declared within a generic type declaration (§14.3.9.7), if a base class is specified in the generic class declaration, the base class of the constructed type is obtained by substituting, for each type_parameter in the base class declaration, the corresponding type_argument of the constructed type.
The base class specified in a class declaration can be a constructed class type (§8.4). A base class cannot be a type parameter on its own (§8.5), though it can involve the type parameters that are in scope.
The direct base class of a class type shall be at least as accessible as the class type itself (§7.5.5). For example, it is a compile-time error for a public class to derive from a private or internal class. The direct base class of a class type shall not be any of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType. Furthermore, a generic class declaration shall not use System.Attribute as a direct or indirect base class (§21.2.1). In determining the meaning of the direct base class specification A of a class B, the direct base class of B is temporarily assumed to be object, which ensures that the meaning of a base class specification cannot recursively depend on itself.
The base classes of a class are the direct base class and its base classes. In other words, the set of base classes is the transitive closure of the direct base class relationship.
Except for class object, every class has exactly one direct base class. The object class has no direct base class and is the ultimate base class of all other classes. It is a compile-time error for a class to depend on itself. For the purpose of this rule, a class directly depends on its direct base class (if any) and directly depends on the nearest enclosing class within which it is nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the directly depends on relationship.
A class does not depend on the classes that are nested within it.
It is not possible to derive from a sealed class.
14.2.4.3 Interface implementationsA class_base specification may include a list of interface types, in which case the class is said to implement the given interface types. For a constructed class type, including a nested type declared within a generic type declaration (§14.3.9.7), each implemented interface type is obtained by substituting, for each type_parameter in the given interface, the corresponding type_argument of the constructed type. The set of interfaces for a type declared in multiple parts (§14.2.7) is the union of the interfaces specified on each part. A particular interface can only be named once on each part, but multiple parts can name the same base interface(s). There shall only be one implementation of each member of any given interface.
Typically, each part provides an implementation of the interface(s) declared on that part; however, this is not a requirement. A part can provide the implementation for an interface declared on a different part.
The base interfaces specified in a class declaration can be constructed interface types (§8.4, §17.2). A base interface cannot be a type parameter on its own, though it can involve the type parameters that are in scope.
Interface implementations are discussed further in §17.6. 14.2.5 Type parameter constraintsGeneric type and method declarations can optionally specify type parameter constraints by including type_parameter_constraints_clauses. type_parameter_constraints_clauses : type_parameter_constraints_clause | type_parameter_constraints_clauses type_parameter_constraints_clause ; type_parameter_constraints_clause : 'where' type_parameter ':' type_parameter_constraints ; type_parameter_constraints : primary_constraint | secondary_constraints | constructor_constraint | primary_constraint ',' secondary_constraints | primary_constraint ',' constructor_constraint | secondary_constraints ',' constructor_constraint | primary_constraint ',' secondary_constraints ',' constructor_constraint ; primary_constraint : class_type | 'class' | 'struct' ; secondary_constraints : interface_type | type_parameter | secondary_constraints ',' interface_type | secondary_constraints ',' type_parameter ; constructor_constraint : 'new' '(' ')' ;Each type_parameter_constraints_clause consists of the token where, followed by the name of a type parameter, followed by a colon and the list of constraints for that type parameter. There can be at most one where clause for each type parameter, and the where clauses can be listed in any order. Like the get and set tokens in a property accessor, the where token is not a keyword. The list of constraints given in a where clause can include any of the following components, in this order: a single primary constraint, one or more secondary constraints, and the constructor constraint, new(). A primary constraint can be a class type or the reference type constraint class or the value type constraint struct. A secondary constraint can be a type_parameter or interface_type. The reference type constraint specifies that a type argument used for the type parameter shall be a reference type. All class types, interface types, delegate types, array types, and type parameters known to be a reference type (as defined below) satisfy this constraint. The value type constraint specifies that a type argument used for the type parameter shall be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable value type (§8.3.11) does not satisfy the value type constraint. A type parameter having the value type constraint shall not also have the constructor_constraint, although it may be used as a type argument for another type parameter with a constructor_constraint.
Pointer types are never allowed to be type arguments and are not considered to satisfy either the reference type or value type constraints. If a constraint is a class type, an interface type, or a type parameter, that type specifies a minimal “base type” that every type argument used for that type parameter shall support. Whenever a constructed type or generic method is used, the type argument is checked against the constraints on the type parameter at compile-time. The type argument supplied shall satisfy the conditions described in §8.4.5. A class_type constraint shall satisfy the following rules:
A type specified as an interface_type constraint shall satisfy the following rules:
In either case, the constraint may involve any of the type parameters of the associated type or method declaration as part of a constructed type, and may involve the type being declared. Any class or interface type specified as a type parameter constraint shall be at least as accessible (§7.5.5) as the generic type or method being declared. A type specified as a type_parameter constraint shall satisfy the following rules:
In addition there shall be no cycles in the dependency graph of type parameters, where dependency is a transitive relation defined by:
Given this relation, it is a compile-time error for a type parameter to depend on itself (directly or indirectly). Any constraints shall be consistent among dependent type parameters. If type parameter S depends on type parameter T then:
It is valid for S to have the value type constraint and T to have the reference type constraint. Effectively this limits T to the types System.Object, System.ValueType, System.Enum, and any interface type. If the where clause for a type parameter includes a constructor constraint (which has the form new()), it is possible to use the new operator to create instances of the type (§11.7.15.2). Any type argument used for a type parameter with a constructor constraint shall be a value type, a non-abstract class having a public parameterless constructor, or a type parameter having the value type constraint or constructor constraint. It is a compile-time error for type_parameter_constraints having a primary_constraint of struct to also have a constructor_constraint.
The dynamic erasure of a type C is type Cₓ constructed as follows:
The effective base class of a type parameter T is defined as follows: Let R be a set of types such that:
Then
If the type parameter is a method type parameter whose constraints are inherited from the base method the effective base class is calculated after type substitution. These rules ensure that the effective base class is always a class_type. The effective interface set of a type parameter T is defined as follows:
A type parameter is known to be a reference type if it has the reference type constraint or its effective base class is not object or System.ValueType. Values of a constrained type parameter type can be used to access the instance members implied by the constraints.
When a partial generic type declaration includes constraints, the constraints shall agree with all other parts that include constraints. Specifically, each part that includes constraints shall have constraints for the same set of type parameters, and for each type parameter, the sets of primary, secondary, and constructor constraints shall be equivalent. Two sets of constraints are equivalent if they contain the same members. If no part of a partial generic type specifies type parameter constraints, the type parameters are considered unconstrained.
14.2.6 Class bodyThe class_body of a class defines the members of that class. class_body : '{' class_member_declaration* '}' ;14.2.7 Partial declarationsThe modifier partial is used when defining a class, struct, or interface type in multiple parts. The partial modifier is a contextual keyword (§6.4.4) and only has special meaning immediately before one of the keywords class, struct, or interface. Each part of a partial type declaration shall include a partial modifier and shall be declared in the same namespace or containing type as the other parts. The partial modifier indicates that additional parts of the type declaration might exist elsewhere, but the existence of such additional parts is not a requirement; it is valid for the only declaration of a type to include the partial modifier. All parts of a partial type shall be compiled together such that the parts can be merged at compile-time. Partial types specifically do not allow already compiled types to be extended. Nested types can be declared in multiple parts by using the partial modifier. Typically, the containing type is declared using partial as well, and each part of the nested type is declared in a different part of the containing type.
The handling of attributes specified on the type or type parameters of different parts of a partial declaration is discussed in §21.3. 14.3 Class members14.3.1 GeneralThe members of a class consist of the members introduced by its class_member_declarations and the members inherited from the direct base class. class_member_declaration : constant_declaration | field_declaration | method_declaration | property_declaration | event_declaration | indexer_declaration | operator_declaration | constructor_declaration | finalizer_declaration | static_constructor_declaration | type_declaration ;The members of a class are divided into the following categories:
A class_declaration creates a new declaration space (§7.3), and the type_parameters and the class_member_declarations immediately contained by the class_declaration introduce new members into this declaration space. The following rules apply to class_member_declarations:
The inherited members of a class (§14.3.4) are not part of the declaration space of a class.
The set of members of a type declared in multiple parts (§14.2.7) is the union of the members declared in each part. The bodies of all parts of the type declaration share the same declaration space (§7.3), and the scope of each member (§7.7) extends to the bodies of all the parts. The accessibility domain of any member always includes all the parts of the enclosing type; a private member declared in one part is freely accessible from another part. It is a compile-time error to declare the same member in more than one part of the type, unless that member is a type having the partial modifier.
Field initialization order can be significant within C# code, and some guarantees are provided, as defined in §14.5.6.1. Otherwise, the ordering of members within a type is rarely significant, but may be significant when interfacing with other languages and environments. In these cases, the ordering of members within a type declared in multiple parts is undefined. 14.3.2 The instance typeEach class declaration has an associated instance type. For a generic class declaration, the instance type is formed by creating a constructed type (§8.4) from the type declaration, with each of the supplied type arguments being the corresponding type parameter. Since the instance type uses the type parameters, it can only be used where the type parameters are in scope; that is, inside the class declaration. The instance type is the type of this for code written inside the class declaration. For non-generic classes, the instance type is simply the declared class.
14.3.3 Members of constructed typesThe non-inherited members of a constructed type are obtained by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the constructed type. The substitution process is based on the semantic meaning of type declarations, and is not simply textual substitution.
Within instance function members, the type of this is the instance type (§14.3.2) of the containing declaration. All members of a generic class can use type parameters from any enclosing class, either directly or as part of a constructed type. When a particular closed constructed type (§8.4.3) is used at run-time, each use of a type parameter is replaced with the type argument supplied to the constructed type.
14.3.4 InheritanceA class inherits the members of its direct base class. Inheritance means that a class implicitly contains all members of its direct base class, except for the instance constructors, finalizers, and static constructors of the base class. Some important aspects of inheritance are:
The inherited members of a constructed class type are the members of the immediate base class type (§14.2.4.2), which is found by substituting the type arguments of the constructed type for each occurrence of the corresponding type parameters in the base_class_specification. These members, in turn, are transformed by substituting, for each type_parameter in the member declaration, the corresponding type_argument of the base_class_specification.
14.3.5 The new modifierA class_member_declaration is permitted to declare a member with the same name or signature as an inherited member. When this occurs, the derived class member is said to hide the base class member. See §7.7.2.3 for a precise specification of when a member hides an inherited member. An inherited member M is considered to be available if M is accessible and there is no other inherited accessible member N that already hides M. Implicitly hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning unless the declaration of the derived class member includes a new modifier to explicitly indicate that the derived member is intended to hide the base member. If one or more parts of a partial declaration (§14.2.7) of a nested type include the new modifier, no warning is issued if the nested type hides an available inherited member. If a new modifier is included in a declaration that doesn’t hide an available inherited member, a warning to that effect is issued. 14.3.6 Access modifiersA class_member_declaration can have any one of the permitted kinds of declared accessibility (§7.5.2): public, protected internal, protected, private protected, internal, or private. Except for the protected internal and private protected combinations, it is a compile-time error to specify more than one access modifier. When a class_member_declaration does not include any access modifiers, private is assumed. 14.3.7 Constituent typesTypes that are used in the declaration of a member are called the constituent types of that member. Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or instance constructor. The constituent types of a member shall be at least as accessible as that member itself (§7.5.5). 14.3.8 Static and instance membersMembers of a class are either static members or instance members.
When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member. Static members have the following characteristics:
When a field, method, property, event, indexer, constructor, or finalizer declaration does not include a static modifier, it declares an instance member. (An instance member is sometimes called a non-static member.) Instance members have the following characteristics:
14.3.9 Nested types14.3.9.1 GeneralA type declared within a class or struct is called a nested type. A type that is declared within a compilation unit or namespace is called a non-nested type.
14.3.9.2 Fully qualified nameThe fully qualified name (§7.8.3) for a nested type declarationis S.N where S is the fully qualified name of the type declarationin which type N is declared and N is the unqualified name (§7.8.2) of the nested type declaration (including any generic_dimension_specifier (§11.7.16)). 14.3.9.3 Declared accessibilityNon-nested types can have public or internal declared accessibility and have internal declared accessibility by default. Nested types can have these forms of declared accessibility too, plus one or more additional forms of declared accessibility, depending on whether the containing type is a class or struct:
14.3.9.4 HidingA nested type may hide (§7.7.2.2) a base member. The new modifier (§14.3.5) is permitted on nested type declarations so that hiding can be expressed explicitly.
14.3.9.5 this accessA nested type and its containing type do not have a special relationship with regard to this_access (§11.7.12). Specifically, this within a nested type cannot be used to refer to instance members of the containing type. In cases where a nested type needs access to the instance members of its containing type, access can be provided by providing the this for the instance of the containing type as a constructor argument for the nested type.
14.3.9.6 Access to private and protected members of the containing typeA nested type has access to all of the members that are accessible to its containing type, including members of the containing type that have private and protected declared accessibility.
A nested type also may access protected members defined in a base type of its containing type.
14.3.9.7 Nested types in generic classesA generic class declaration may contain nested type declarations. The type parameters of the enclosing class may be used within the nested types. A nested type declaration may contain additional type parameters that apply only to the nested type. Every type declaration contained within a generic class declaration is implicitly a generic type declaration. When writing a reference to a type nested within a generic type, the containing constructed type, including its type arguments, shall be named. However, from within the outer class, the nested type may be used without qualification; the instance type of the outer class may be implicitly used when constructing the nested type.
Although it is bad programming style, a type parameter in a nested type can hide a member or type parameter declared in the outer type.
14.3.10 Reserved member names14.3.10.1 GeneralTo facilitate the underlying C# run-time implementation, for each source member declaration that is a property, event, or indexer, the implementation shall reserve two method signatures based on the kind of the member declaration, its name, and its type (§14.3.10.2, §14.3.10.3, §14.3.10.4). It is a compile-time error for a program to declare a member whose signature matches a signature reserved by a member declared in the same scope, even if the underlying run-time implementation does not make use of these reservations. The reserved names do not introduce declarations, thus they do not participate in member lookup. However, a declaration’s associated reserved method signatures do participate in inheritance (§14.3.4), and can be hidden with the new modifier (§14.3.5).
The declaration of a finalizer (§14.13) also causes a signature to be reserved (§14.3.10.5). 14.3.10.2 Member names reserved for propertiesFor a property P (§14.7) of type T, the following signatures are reserved: T get_P(); void set_P(T value);Both signatures are reserved, even if the property is read-only or write-only.
14.3.10.3 Member names reserved for eventsFor an event E (§14.8) of delegate type T, the following signatures are reserved: void add_E(T handler); void remove_E(T handler);14.3.10.4 Member names reserved for indexersFor an indexer (§14.9) of type T with parameter-list L, the following signatures are reserved: T get_Item(L); void set_Item(L, T value);Both signatures are reserved, even if the indexer is read-only or write-only. Furthermore the member name Item is reserved. 14.3.10.5 Member names reserved for finalizersFor a class containing a finalizer (§14.13), the following signature is reserved: void Finalize();14.4 ConstantsA constant is a class member that represents a constant value: a value that can be computed at compile-time. A constant_declaration introduces one or more constants of a given type. constant_declaration : attributes? constant_modifier* 'const' type constant_declarators ';' ; constant_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' ;A constant_declaration may include a set of attributes (§21), a new modifier (§14.3.5), and any one of the permitted kinds of declared accessibility (§14.3.6). The attributes and modifiers apply to all of the members declared by the constant_declaration. Even though constants are considered static members, a constant_declaration neither requires nor allows a static modifier. It is an error for the same modifier to appear multiple times in a constant declaration. The type of a constant_declaration specifies the type of the members introduced by the declaration. The type is followed by a list of constant_declarators (§12.6.3), each of which introduces a new member. A constant_declarator consists of an identifier that names the member, followed by an “=” token, followed by a constant_expression (§11.20) that gives the value of the member. The type specified in a constant declaration shall be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum_type, or a reference_type. Each constant_expression shall yield a value of the target type or of a type that can be converted to the target type by an implicit conversion (§10.2). The type of a constant shall be at least as accessible as the constant itself (§7.5.5). The value of a constant is obtained in an expression using a simple_name (§11.7.4) or a member_access (§11.7.6). A constant can itself participate in a constant_expression. Thus, a constant may be used in any construct that requires a constant_expression.
When a symbolic name for a constant value is desired, but when the type of that value is not permitted in a constant declaration, or when the value cannot be computed at compile-time by a constant_expression, a readonly field (§14.5.3) may be used instead.
A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type.
Constants are permitted to depend on other constants within the same program as long as the dependencies are not of a circular nature. The compiler automatically arranges to evaluate the constant declarations in the appropriate order.
Constant declarations may depend on constants from other programs, but such dependencies are only possible in one direction.
14.5 Fields14.5.1 GeneralA field is a member that represents a variable associated with an object or class. A field_declaration introduces one or more fields of a given type. field_declaration : attributes? field_modifier* type variable_declarators ';' ; field_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'static' | 'readonly' | 'volatile' | unsafe_modifier // unsafe code support ; variable_declarators : variable_declarator (',' variable_declarator)* ; variable_declarator : identifier ('=' variable_initializer)? ;unsafe_modifier (§22.2) is only available in unsafe code (§22). A field_declaration may include a set of attributes (§21), a new modifier (§14.3.5), a valid combination of the four access modifiers (§14.3.6), and a static modifier (§14.5.2). In addition, a field_declaration may include a readonly modifier (§14.5.3) or a volatile modifier (§14.5.4), but not both. The attributes and modifiers apply to all of the members declared by the field_declaration. It is an error for the same modifier to appear multiple times in a field_declaration. The type of a field_declaration specifies the type of the members introduced by the declaration. The type is followed by a list of variable_declarators, each of which introduces a new member. A variable_declarator consists of an identifier that names that member, optionally followed by an “=” token and a variable_initializer (§14.5.6) that gives the initial value of that member. The type of a field shall be at least as accessible as the field itself (§7.5.5). The value of a field is obtained in an expression using a simple_name (§11.7.4), a member_access (§11.7.6) or a base_access (§11.7.13). The value of a non-readonly field is modified using an assignment (§11.18). The value of a non-readonly field can be both obtained and modified using postfix increment and decrement operators (§11.7.14) and prefix increment and decrement operators (§11.8.6). A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type.
14.5.2 Static and instance fieldsWhen a field declaration includes a static modifier, the fields introduced by the declaration are static fields. When no static modifier is present, the fields introduced by the declaration are instance fields. Static fields and instance fields are two of the several kinds of variables (§9) supported by C#, and at times they are referred to as static variables and instance variables, respectively. As explained in §14.3.8, each instance of a class contains a complete set of the instance fields of the class, while there is only one set of static fields for each non-generic class or closed constructed type, regardless of the number of instances of the class or closed constructed type. 14.5.3 Readonly fields14.5.3.1 GeneralWhen a field_declaration includes a readonly modifier, the fields introduced by the declaration are readonly fields. Direct assignments to readonly fields can only occur as part of that declaration or in an instance constructor or static constructor in the same class. (A readonly field can be assigned to multiple times in these contexts.) Specifically, direct assignments to a readonly field are permitted only in the following contexts:
Attempting to assign to a readonly field or pass it as an out or ref parameter in any other context is a compile-time error. 14.5.3.2 Using static readonly fields for constantsA static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration, or when the value cannot be computed at compile-time.
14.5.3.3 Versioning of constants and static readonly fieldsConstants and readonly fields have different binary versioning semantics. When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time.
14.5.4 Volatile fieldsWhen a field_declaration includes a volatile modifier, the fields introduced by that declaration are volatile fields. For non-volatile fields, optimization techniques that reorder instructions can lead to unexpected and unpredictable results in multi-threaded programs that access fields without synchronization such as that provided by the lock_statement (§12.13). These optimizations can be performed by the compiler, by the run-time system, or by hardware. For volatile fields, such reordering optimizations are restricted:
These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. The type of a volatile field shall be one of the following:
14.5.5 Field initializationThe initial value of a field, whether it be a static field or an instance field, is the default value (§9.3) of the field’s type. It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never “uninitialized”.
14.5.6 Variable initializers14.5.6.1 GeneralField declarations may include variable_initializers. For static fields, variable initializers correspond to assignment statements that are executed during class initialization. For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.
The default value initialization described in §14.5.5 occurs for all fields, including fields that have variable initializers. Thus, when a class is initialized, all static fields in that class are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields in that instance are first initialized to their default values, and then the instance field initializers are executed in textual order. When there are field declarations in multiple partial type declarations for the same type, the order of the parts is unspecified. However, within each part the field initializers are executed in order. It is possible for static fields with variable initializers to be observed in their default value state.
14.5.6.2 Static field initializationThe static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration (§14.5.6.1). Within a partial class, the meaning of “textual order” is specified by §14.5.6.1. If a static constructor (§14.12) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.
14.5.6.3 Instance field initializationThe instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to any one of the instance constructors (§14.11.3) of that class. Within a partial class, the meaning of “textual order” is specified by §14.5.6.1. The variable initializers are executed in the textual order in which they appear in the class declaration (§14.5.6.1). The class instance creation and initialization process is described further in §14.11. A variable initializer for an instance field cannot reference the instance being created. Thus, it is a compile-time error to reference this in a variable initializer, as it is a compile-time error for a variable initializer to reference any instance member through a simple_name.
14.6 Methods14.6.1 GeneralA method is a member that implements a computation or action that can be performed by an object or class. Methods are declared using method_declarations: method_declaration : method_header method_body ; method_header : attributes? method_modifier* 'partial'? return_type member_name type_parameter_list? '(' formal_parameter_list? ')' type_parameter_constraints_clause* ; method_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'static' | 'virtual' | 'sealed' | 'override' | 'abstract' | 'extern' | 'async' | unsafe_modifier // unsafe code support ; return_type : type | 'void' ; member_name : identifier | interface_type '.' identifier ; method_body : block | '=>' null_conditional_invocation_expression ';' | '=>' expression ';' | ';' ;Grammar notes:
A method_declaration may include a set of attributes (§21) and one of the permitted kinds of declared accessibility (§14.3.6), the new (§14.3.5), static (§14.6.3), virtual (§14.6.4), override (§14.6.5), sealed (§14.6.6), abstract (§14.6.7), extern (§14.6.8) and async (§14.15) modifiers. A declaration has a valid combination of modifiers if all of the following are true:
The return_type of a method declaration specifies the type of the value computed and returned by the method. The return_type is void if the method does not return a value. If the declaration includes the partial modifier, then the return type shall be void (§14.6.9). If the declaration includes the async modifier then the return type shall be void or a task type (§14.15.1). A generic method is a method whose declaration includes a type_parameter_list. This specifies the type parameters for the method. The optional type_parameter_constraints_clauses specify the constraints for the type parameters. A method_declaration shall not have type_parameter_constraints_clauses unless it also has a type_parameter_list. A method_declaration for an explicit interface member implementation shall not have any type_parameter_constraints_clauses. A generic method_declaration for an explicit interface member implementation inherits any constraints from the constraints on the interface method. Similarly, a method declaration with the override modifier shall not have any type_parameter_constraints_clauses and the constraints of the method’s type parameters are inherited from the virtual method being overridden.The member_name specifies the name of the method. Unless the method is an explicit interface member implementation (§17.6.2), the member_name is simply an identifier. For an explicit interface member implementation, the member_name consists of an interface_type followed by a “.” and an identifier. In this case, the declaration shall include no modifiers other than (possibly) extern or async. The optional formal_parameter_list specifies the parameters of the method (§14.6.2). The return_type and each of the types referenced in the formal_parameter_list of a method shall be at least as accessible as the method itself (§7.5.5). The method_body is either a semicolon, a block body or an expression body. A block body consists of a block, which specifies the statements to execute when the method is invoked. An expression body consists of =>, followed by a null_conditional_invocation_expression or expression, followed by a semicolon, and denotes a single expression to perform when the method is invoked. For abstract and extern methods, the method_body consists simply of a semicolon. For partial methods the method_body may consist of either a semicolon, a block body or an expression body. For all other methods, the method_body is either a block body or an expression body. If the method_body consists of a semicolon, the declaration shall not include the async modifier. The name, the number of type parameters, and the formal parameter list of a method define the signature (§7.6) of the method. Specifically, the signature of a method consists of its name, the number of its type parameters, and the number, parameter_mode_modifiers (§14.6.2.1), and types of its formal parameters. The return type is not part of a method’s signature, nor are the names of the formal parameters, the names of the type parameters, or the constraints. When a formal parameter type references a type parameter of the method, the ordinal position of the type parameter (not the name of the type parameter) is used for type equivalence. The name of a method shall differ from the names of all other non-methods declared in the same class. In addition, the signature of a method shall differ from the signatures of all other methods declared in the same class, and two methods declared in the same class may not have signatures that differ solely by ref and out. The method’s type_parameters are in scope throughout the method_declaration, and can be used to form types throughout that scope in return_type, method_body, and type_parameter_constraints_clauses but not in attributes. All formal parameters and type parameters shall have different names. 14.6.2 Method parameters14.6.2.1 GeneralThe parameters of a method, if any, are declared by the method’s formal_parameter_list. formal_parameter_list : fixed_parameters | fixed_parameters ',' parameter_array | parameter_array ; fixed_parameters : fixed_parameter (',' fixed_parameter)* ; fixed_parameter : attributes? parameter_modifier? type identifier default_argument? ; default_argument : '=' expression ; parameter_modifier : parameter_mode_modifier | 'this' ; parameter_mode_modifier : 'ref' | 'out' ; parameter_array : attributes? 'params' array_type identifier ;The formal parameter list consists of one or more comma-separated parameters of which only the last may be a parameter_array. A fixed_parameter consists of an optional set of attributes (§21); an optional ref, out, or this modifier; a type; an identifier; and an optional default_argument. Each fixed_parameter declares a parameter of the given type with the given name. The this modifier designates the method as an extension method and is only allowed on the first parameter of a static method in a non-generic, non-nested static class. Extension methods are further described in §14.6.10. A fixed_parameter with a default_argument is known as an optional parameter, whereas a fixed_parameter without a default_argument is a required parameter. A required parameter may not appear after an optional parameter in a formal_parameter_list. A parameter with a ref, out or this modifier cannot have a default_argument. The expression in a default_argument shall be one of the following:
The expression shall be implicitly convertible by an identity or nullable conversion to the type of the parameter. If optional parameters occur in an implementing partial method declaration (§14.6.9), an explicit interface member implementation (§17.6.2), a single-parameter indexer declaration (§14.9), or in an operator declaration (§14.10.1) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted. A parameter_array consists of an optional set of attributes (§21), a params modifier, an array_type, and an identifier. A parameter array declares a single parameter of the given array type with the given name. The array_type of a parameter array shall be a single-dimensional array type (§16.2). In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Parameter arrays are described further in §14.6.2.5. A parameter_array may occur after an optional parameter, but cannot have a default value – the omission of arguments for a parameter_array would instead result in the creation of an empty array.
A method declaration creates a separate declaration space (§7.3) for parameters and type parameters. Names are introduced into this declaration space by the type parameter list and the formal parameter list of the method. The body of the method, if any, is considered to be nested within this declaration space. It is an error for two members of a method declaration space to have the same name. It is an error for the method declaration space and the local variable declaration space of a nested declaration space to contain elements with the same name. A method invocation (§11.7.8.2) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Within the block of a method, formal parameters can be referenced by their identifiers in simple_name expressions (§11.7.4). There are four kinds of formal parameters:
14.6.2.2 Value parametersA parameter declared with no modifiers is a value parameter. A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation. When a formal parameter is a value parameter, the corresponding argument in a method invocation shall be an expression that is implicitly convertible (§10.2) to the formal parameter type. A method is permitted to assign new values to a value parameter. Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation. 14.6.2.3 Reference parametersA parameter declared with a ref modifier is a reference parameter. Unlike a value parameter, a reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation. When a formal parameter is a reference parameter, the corresponding argument in a method invocation shall consist of the keyword ref followed by a variable_reference (§9.5) of the same type as the formal parameter. A variable shall be definitely assigned before it can be passed as a reference parameter. Within a method, a reference parameter is always considered definitely assigned. A method declared as an iterator (§14.14) may not have reference parameters.
In a method that takes reference parameters, it is possible for multiple names to represent the same storage location.
14.6.2.4 Output parametersA parameter declared with an out modifier is an output parameter. Similar to a reference parameter, an output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation. When a formal parameter is an output parameter, the corresponding argument in a method invocation shall consist of the keyword out followed by a variable_reference (§9.5) of the same type as the formal parameter. A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned. Within a method, just like a local variable, an output parameter is initially considered unassigned and shall be definitely assigned before its value is used. Every output parameter of a method shall be definitely assigned before the method returns. A method declared as a partial method (§14.6.9) or an iterator (§14.14) may not have output parameters. Output parameters are typically used in methods that produce multiple return values.
14.6.2.5 Parameter arraysA parameter declared with a params modifier is a parameter array. If a formal parameter list includes a parameter array, it shall be the last parameter in the list and it shall be of a single-dimensional array type.
It is not possible to combine the params modifier with the modifiers ref and out. A parameter array permits arguments to be specified in one of two ways in a method invocation:
Except for allowing a variable number of arguments in an invocation, a parameter array is precisely equivalent to a value parameter (§14.6.2.2) of the same type.
When performing overload resolution, a method with a parameter array might be applicable, either in its normal form or in its expanded form (§11.6.4.2). The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type.
When the type of a parameter array is object[], a potential ambiguity arises between the normal form of the method and the expanded form for a single object parameter. The reason for the ambiguity is that an object[] is itself implicitly convertible to type object. The ambiguity presents no problem, however, since it can be resolved by inserting a cast if needed.
14.6.3 Static and instance methodsWhen a method declaration includes a static modifier, that method is said to be a static method. When no static modifier is present, the method is said to be an instance method. A static method does not operate on a specific instance, and it is a compile-time error to refer to this in a static method. An instance method operates on a given instance of a class, and that instance can be accessed as this (§11.7.12). The differences between static and instance members are discussed further in §14.3.8. 14.6.4 Virtual methodsWhen an instance method declaration includes a virtual modifier, that method is said to be a virtual method. When no virtual modifier is present, the method is said to be a non-virtual method. The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. In contrast, the implementation of a virtual method can be superseded by derived classes. The process of superseding the implementation of an inherited virtual method is known as overriding that method (§14.6.5). In a virtual method invocation, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. In a non-virtual method invocation, the compile-time type of the instance is the determining factor. In precise terms, when a method named N is invoked with an argument list A on an instance with a compile-time type C and a run-time type R (where R is either C or a class derived from C), the invocation is processed as follows:
For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. The most derived implementation of a virtual method M with respect to a class R is determined as follows:
Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. This does not present an ambiguity problem, since all but the most derived method are hidden.
14.6.5 Override methodsWhen an instance method declaration includes an override modifier, the method is said to be an override method. An override method overrides an inherited virtual method with the same signature. Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of that method. The method overridden by an override declaration is known as the overridden base method For an override method M declared in a class C, the overridden base method is determined by examining each base class of C, starting with the direct base class of C and continuing with each successive direct base class, until in a given base class type at least one accessible method is located which has the same signature as M after substitution of type arguments. For the purposes of locating the overridden base method, a method is considered accessible if it is public, if it is protected, if it is protected internal, or if it is either internal or private protected and declared in the same program as C. A compile-time error occurs unless all of the following are true for an override declaration:
An override declaration can access the overridden base method using a base_access (§11.7.13).
Only by including an override modifier can a method override another method. In all other cases, a method with the same signature as an inherited method simply hides the inherited method.
14.6.6 Sealed methodsWhen an instance method declaration includes a sealed modifier, that method is said to be a sealed method. A sealed method overrides an inherited virtual method with the same signature. A sealed method shall also be marked with the override modifier. Use of the sealed modifier prevents a derived class from further overriding the method.
14.6.7 Abstract methodsWhen an instance method declaration includes an abstract modifier, that method is said to be an abstract method. Although an abstract method is implicitly also a virtual method, it cannot have the modifier virtual. An abstract method declaration introduces a new virtual method but does not provide an implementation of that method. Instead, non-abstract derived classes are required to provide their own implementation by overriding that method. Because an abstract method provides no actual implementation, the method_body of an abstract method simply consists of a semicolon. Abstract method declarations are only permitted in abstract classes (§14.2.2.2).
It is a compile-time error for a base_access (§11.7.13) to reference an abstract method.
An abstract method declaration is permitted to override a virtual method. This allows an abstract class to force re-implementation of the method in derived classes, and makes the original implementation of the method unavailable.
14.6.8 External methodsWhen a method declaration includes an extern modifier, the method is said to be an external method. External methods are implemented externally, typically using a language other than C#. Because an external method declaration provides no actual implementation, the method_body of an external method simply consists of a semicolon. An external method shall not be generic. The mechanism by which linkage to an external method is achieved, is implementation-defined.
14.6.9 Partial methodsWhen a method declaration includes a partial modifier, that method is said to be a partial method. Partial methods may only be declared as members of partial types (§14.2.7), and are subject to a number of restrictions. Partial methods may be defined in one part of a type declaration and implemented in another. The implementation is optional; if no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts. Partial methods shall not define access modifiers; they are implicitly private. Their return type shall be void, and their parameters shall not have the out modifier. The identifier partial is recognized as a contextual keyword (§6.4.4) in a method declaration only if it appears immediately before the void keyword. A partial method cannot explicitly implement interface methods. There are two kinds of partial method declarations: If the body of the method declaration is a semicolon, the declaration is said to be a defining partial method declaration. If the body is other than a semicolon, the declaration is said to be an implementing partial method declaration. Across the parts of a type declaration, there may be only one defining partial method declaration with a given signature, and there may be only one implementing partial method declaration with a given signature. If an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following:
An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration. Only a defining partial method participates in overload resolution. Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Because a partial method always returns void, such invocation expressions will always be expression statements. Furthermore, because a partial method is implicitly private, such statements will always occur within one of the parts of the type declaration within which the partial method is declared.
If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. Thus the invocation expression, including any subexpressions, has no effect at run-time. The partial method itself is also removed and will not be a member of the combined type declaration. If an implementing declaration exists for a given partial method, the invocations of the partial methods are retained. The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:
If a defining declaration but not an implementing declaration is given for a partial method M, the following restrictions apply:
Partial methods are useful for allowing one part of a type declaration to customize the behavior of another part, e.g., one that is generated by a tool. Consider the following partial class declaration: partial class Customer { string name; public string Name { get => name; set { OnNameChanging(value); name = value; OnNameChanged(); } } partial void OnNameChanging(string newName); partial void OnNameChanged(); }If this class is compiled without any other parts, the defining partial method declarations and their invocations will be removed, and the resulting combined class declaration will be equivalent to the following: class Customer { string name; public string Name { get => name; set => name = value; } }Assume that another part is given, however, which provides implementing declarations of the partial methods: partial class Customer { partial void OnNameChanging(string newName) => Console.WriteLine($"Changing {name} to {newName}"); partial void OnNameChanged() => Console.WriteLine($"Changed to {name}"); }Then the resulting combined class declaration will be equivalent to the following: 14.6.10 Extension methodsWhen the first parameter of a method includes the this modifier, that method is said to be an extension method. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method may have no modifiers other than this, and the parameter type may not be a pointer type.
An extension method is a regular static method. In addition, where its enclosing static class is in scope, an extension method may be invoked using instance method invocation syntax (§11.7.8.3), using the receiver expression as the first argument.
14.6.11 Method bodyThe method_body of a method declaration consists of either a block body, an expression body or a semicolon. Abstract and external method declarations do not provide a method implementation, so their method bodies simply consist of a semicolon. For any other method, the method body is a block (§12.3) that contains the statements to execute when that method is invoked. The effective return type of a method is void if the return type is void, or if the method is async and the return type is System.Threading.Tasks.Task. Otherwise, the effective return type of a non-async method is its return type, and the effective return type of an async method with return type System.Threading.Tasks.Task<T> is T. When the effective return type of a method is void and the method has a block body, return statements (§12.10.5) in the block shall not specify an expression. If execution of the block of a void method completes normally (that is, control flows off the end of the method body), that method simply returns to its caller. When the effective return type of a method is void and the method has an expression body, the expression E shall be a statement_expression, and the body is exactly equivalent to a statment body of the form { E; }. When the effective return type of a method is not void and the method has a block body, each return statement in that method’s body shall specify an expression that is implicitly convertible to the effective return type. The endpoint of the method body of a value-returning method shall not be reachable. In other words, in a value-returning method with a block body, control is not permitted to flow off the end of the method body. When the effective return type of a method is not void and the method has an expression body, E, the expression shall be implicitly convertible to the effective return type, and the body is exactly equivalent to a block body of the form { return E; }.
14.7 Properties14.7.1 GeneralA property is a member that provides access to a characteristic of an object or a class. Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. Properties are a natural extension of fields—both are named members with associated types, and the syntax for accessing fields and properties is the same. However, unlike fields, properties do not denote storage locations. Instead, properties have accessors that specify the statements to be executed when their values are read or written. Properties thus provide a mechanism for associating actions with the reading and writing of an object’s characteristics; furthermore, they permit such characteristics to be computed. Properties are declared using property_declarations: property_declaration : attributes? property_modifier* type member_name property_body ; property_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'static' | 'virtual' | 'sealed' | 'override' | 'abstract' | 'extern' | unsafe_modifier // unsafe code support ; property_body : '{' accessor_declarations '}' property_initializer? | '=>' expression ';' ; property_initializer : '=' variable_initializer ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). A property_declaration may include a set of attributes (§21) and any one of the permitted kinds of declared accessibility (§14.3.6), the new (§14.3.5), static (§14.7.2), virtual (§14.6.4, §14.7.6), override (§14.6.5, §14.7.6), sealed (§14.6.6), abstract (§14.6.7, §14.7.6), and extern (§14.6.8) modifiers. Property declarations are subject to the same rules as method declarations (§14.6) with regard to valid combinations of modifiers. The type of a property declaration specifies the type of the property introduced by the declaration, and the member_name (§14.6.1) specifies the name of the property. Unless the property is an explicit interface member implementation, the member_name is simply an identifier. For an explicit interface member implementation (§17.6.2), the member_name consists of an interface_type followed by a “.” and an identifier. The type of a property shall be at least as accessible as the property itself (§7.5.5). A property_body may either consist of an accessor body or an expression body. In an accessor body, accessor_declarations, which shall be enclosed in “{” and “}” tokens, declare the accessors (§14.7.3) of the property. The accessors specify the executable statements associated with reading and writing the property. An expression body consisting of => followed by an expression E and a semicolon is exactly equivalent to the statement body { get { return E; } }, and can therefore only be used to specify read-only properties where the result of the get accessor is given by a single expression. A property_initializer may only be given for an automatically implemented property (§14.7.4), and causes the initialization of the underlying field of such properties with the value given by the expression. Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as a ref or out argument. When a property declaration includes an extern modifier, the property is said to be an external property. Because an external property declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon. 14.7.2 Static and instance propertiesWhen a property declaration includes a static modifier, the property is said to be a static property. When no static modifier is present, the property is said to be an instance property. A static property is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static property. An instance property is associated with a given instance of a class, and that instance can be accessed as this (§11.7.12) in the accessors of that property. The differences between static and instance members are discussed further in §14.3.8. 14.7.3 AccessorsThe accessor_declarations of a property specify the executable statements associated with reading and writing that property. accessor_declarations : get_accessor_declaration set_accessor_declaration? | set_accessor_declaration get_accessor_declaration? ; get_accessor_declaration : attributes? accessor_modifier? 'get' accessor_body ; set_accessor_declaration : attributes? accessor_modifier? 'set' accessor_body ; accessor_modifier : 'protected' | 'internal' | 'private' | 'protected' 'internal' | 'internal' 'protected' | 'protected' 'private' | 'private' 'protected' ; accessor_body : block | '=>' expression ';' | ';' ;The accessor declarations consist of a get_accessor_declaration, a set_accessor_declaration, or both. Each accessor declaration consists of optional attributes, an optional accessor_modifier, the token get or set, followed by an accessor_body. The use of accessor_modifiers is governed by the following restrictions:
For abstract and extern properties, the accessor_body for each accessor specified is simply a semicolon. A non-abstract, non-extern property may also have the accessor_body for all accessors specified be a semicolon, in which case it is an automatically implemented property (§14.7.4). An automatically implemented property shall have at least a get accessor. For the accessors of any other non-abstract, non-extern property, the accessor_body is either
A get accessor corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property (§11.2.2). The body of a get accessor shall conform to the rules for value-returning methods described in §14.6.11. In particular, all return statements in the body of a get accessor shall specify an expression that is implicitly convertible to the property type. Furthermore, the endpoint of a get accessor shall not be reachable. A set accessor corresponds to a method with a single value parameter of the property type and a void return type. The implicit parameter of a set accessor is always named value. When a property is referenced as the target of an assignment (§11.18), or as the operand of ++ or –- (§11.7.14, §11.8.6), the set accessor is invoked with an argument that provides the new value (§11.18.2). The body of a set accessor shall conform to the rules for void methods described in §14.6.11. In particular, return statements in the set accessor body are not permitted to specify an expression. Since a set accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declaration in a set accessor to have that name. Based on the presence or absence of the get and set accessors, a property is classified as follows:
The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately.
When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing.
Unlike public fields, properties provide a separation between an object’s internal state and its public interface.
Properties can be used to delay initialization of a resource until the moment it is first referenced.
14.7.4 Automatically implemented propertiesAn automatically implemented property (or auto-property for short), is a non-abstract, non-extern property with semicolon-only accessor bodies. Auto-properties shall have a get accessor and may optionally have a set accessor. When a property is specified as an automatically implemented property, a hidden backing field is automatically available for the property, and the accessors are implemented to read from and write to that backing field. The hidden backing field is inaccessible, it can be read and written only through the automatically implemented property accessors, even within the containing type. If the auto-property has no set accessor, the backing field is considered readonly (§14.5.3). Just like a readonly field, a read-only auto-property may also be assigned to in the body of a constructor of the enclosing class. Such an assignment assigns directly to the read-only backing field of the property. An auto-property may optionally have a property_initializer, which is applied directly to the backing field as a variable_initializer (§16.7).
14.7.5 AccessibilityIf an accessor has an accessor_modifier, the accessibility domain (§7.5.3) of the accessor is determined using the declared accessibility of the accessor_modifier. If an accessor does not have an accessor_modifier, the accessibility domain of the accessor is determined from the declared accessibility of the property or indexer. The presence of an accessor_modifier never affects member lookup (§11.5) or overload resolution (§11.6.4). The modifiers on the property or indexer always determine which property or indexer is bound to, regardless of the context of the access. Once a particular property or indexer has been selected, the accessibility domains of the specific accessors involved are used to determine if that usage is valid:
An accessor that is used to implement an interface shall not have an accessor_modifier. If only one accessor is used to implement an interface, the other accessor may be declared with an accessor_modifier:
14.7.6 Virtual, sealed, override, and abstract accessorsA virtual property declaration specifies that the accessors of the property are virtual. The virtual modifier applies to all non-private accessors of a property. When an accessor of a virtual property has the private accessor_modifier, the private accessor is implicitly not virtual. An abstract property declaration specifies that the accessors of the property are virtual, but does not provide an actual implementation of the accessors. Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the property. Because an accessor for an abstract property declaration provides no actual implementation, its accessor_body simply consists of a semicolon. An abstract property shall not have a private accessor. A property declaration that includes both the abstract and override modifiers specifies that the property is abstract and overrides a base property. The accessors of such a property are also abstract. Abstract property declarations are only permitted in abstract classes (§14.2.2.2). The accessors of an inherited virtual property can be overridden in a derived class by including a property declaration that specifies an override directive. This is known as an overriding property declaration. An overriding property declaration does not declare a new property. Instead, it simply specializes the implementations of the accessors of an existing virtual property. The override declaration and the overridden base property are required to have the same declared accessibility. In other words, an override declaration may not change the accessibility of the base property. However, if the overridden base property is protected internal and it is declared in a different assembly than the assembly containing the override declaration then the override declaration’s declared accessibility shall be protected. If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors. There shall be an identity conversion between the type of the overriding and the inherited property. An overriding property declaration may include the sealed modifier. Use of this modifier prevents a derived class from further overriding the property. The accessors of a sealed property are also sealed. Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in §14.6.4, §14.6.5, §14.6.6, and §14.6.7 apply as if accessors were methods of a corresponding form:
When a property is declared as an override, any overridden accessors shall be accessible to the overriding code. In addition, the declared accessibility of both the property or indexer itself, and of the accessors, shall match that of the overridden member and accessors.
14.8 Events14.8.1 GeneralAn event is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying event handlers. Events are declared using event_declarations: event_declaration : attributes? event_modifier* 'event' type variable_declarators ';' | attributes? event_modifier* 'event' type member_name '{' event_accessor_declarations '}' ; event_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'static' | 'virtual' | 'sealed' | 'override' | 'abstract' | 'extern' | unsafe_modifier // unsafe code support ; event_accessor_declarations : add_accessor_declaration remove_accessor_declaration | remove_accessor_declaration add_accessor_declaration ; add_accessor_declaration : attributes? 'add' block ; remove_accessor_declaration : attributes? 'remove' block ;unsafe_modifier (§22.2) is only available in unsafe code (§22). An event_declaration may include a set of attributes (§21) and any one of the permitted kinds of declared accessibility (§14.3.6), the new (§14.3.5), static (§14.6.3, §14.8.4), virtual (§14.6.4, §14.8.5), override (§14.6.5, §14.8.5), sealed (§14.6.6), abstract (§14.6.7, §14.8.5), and extern (§14.6.8) modifiers. Event declarations are subject to the same rules as method declarations (§14.6) with regard to valid combinations of modifiers. The type of an event declaration shall be a delegate_type (§8.2.8), and that delegate_type shall be at least as accessible as the event itself (§7.5.5). An event declaration can include event_accessor_declarations. However, if it does not, for non-extern, non-abstract events, the compiler shall supply them automatically (§14.8.2); for extern events, the accessors are provided externally. An event declaration that omits event_accessor_declarations defines one or more events—one for each of the variable_declarators. The attributes and modifiers apply to all of the members declared by such an event_declaration. It is a compile-time error for an event_declaration to include both the abstract modifier and event_accessor_declarations. When an event declaration includes an extern modifier, the event is said to be an external event. Because an external event declaration provides no actual implementation, it is an error for it to include both the extern modifier and event_accessor_declarations. It is a compile-time error for a variable_declarator of an event declaration with an abstract or external modifier to include a variable_initializer. An event can be used as the left-hand operand of the += and -= operators. These operators are used, respectively, to attach event handlers to, or to remove event handlers from an event, and the access modifiers of the event control the contexts in which such operations are permitted. The only operations that are permitted on an event by code that is outside the type in which that event is declared, are += and -=. Therefore, while such code can add and remove handlers for an event, it cannot directly obtain or modify the underlying list of event handlers. In an operation of the form x += y or x –= y, when x is an event the result of the operation has type void (§11.18.4) (as opposed to having the type of x, with the value of x after the assignment, as for other the += and -= operators defined on non-event types). This prevents external code from indirectly examining the underlying delegate of an event.
14.8.2 Field-like eventsWithin the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event shall not be abstract or extern, and shall not explicitly include event_accessor_declarations. Such an event can be used in any context that permits a field. The field contains a delegate (§19), which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains null.
When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock (§9.4.4.19) in the containing object for an instance event, or the type object (§11.7.15.7) for a static event.
14.8.3 Event accessors
The event_accessor_declarations of an event specify the executable statements associated with adding and removing event handlers. The accessor declarations consist of an add_accessor_declaration and a remove_accessor_declaration. Each accessor declaration consists of the token add or remove followed by a block. The block associated with an add_accessor_declaration specifies the statements to execute when an event handler is added, and the block associated with a remove_accessor_declaration specifies the statements to execute when an event handler is removed. Each add_accessor_declaration and remove_accessor_declaration corresponds to a method with a single value parameter of the event type, and a void return type. The implicit parameter of an event accessor is named value. When an event is used in an event assignment, the appropriate event accessor is used. Specifically, if the assignment operator is += then the add accessor is used, and if the assignment operator is –= then the remove accessor is used. In either case, the right-hand operand of the assignment operator is used as the argument to the event accessor. The block of an add_accessor_declaration or a remove_accessor_declaration shall conform to the rules for void methods described in §14.6.9. In particular, return statements in such a block are not permitted to specify an expression. Since an event accessor implicitly has a parameter named value, it is a compile-time error for a local variable or constant declared in an event accessor to have that name.
14.8.4 Static and instance eventsWhen an event declaration includes a static modifier, the event is said to be a static event. When no static modifier is present, the event is said to be an instance event. A static event is not associated with a specific instance, and it is a compile-time error to refer to this in the accessors of a static event. An instance event is associated with a given instance of a class, and this instance can be accessed as this (§11.7.12) in the accessors of that event. The differences between static and instance members are discussed further in §14.3.8. 14.8.5 Virtual, sealed, override, and abstract accessorsA virtual event declaration specifies that the accessors of that event are virtual. The virtual modifier applies to both accessors of an event. An abstract event declaration specifies that the accessors of the event are virtual, but does not provide an actual implementation of the accessors. Instead, non-abstract derived classes are required to provide their own implementation for the accessors by overriding the event. Because an accessor for an abstract event declaration provides no actual implementation, it shall not provide event_accessor_declarations. An event declaration that includes both the abstract and override modifiers specifies that the event is abstract and overrides a base event. The accessors of such an event are also abstract. Abstract event declarations are only permitted in abstract classes (§14.2.2.2). The accessors of an inherited virtual event can be overridden in a derived class by including an event declaration that specifies an override modifier. This is known as an overriding event declaration. An overriding event declaration does not declare a new event. Instead, it simply specializes the implementations of the accessors of an existing virtual event. An overriding event declaration shall specify the exact same accessibility modifiers and name as the overridden event, there shall be an identity conversion between the type of the overriding and the overridden event, and both the add and remove accessors shall be specified within the declaration. An overriding event declaration can include the sealed modifier. Use of this modifier prevents a derived class from further overriding the event. The accessors of a sealed event are also sealed. It is a compile-time error for an overriding event declaration to include a new modifier. Except for differences in declaration and invocation syntax, virtual, sealed, override, and abstract accessors behave exactly like virtual, sealed, override and abstract methods. Specifically, the rules described in §14.6.4, §14.6.5, §14.6.6, and §14.6.7 apply as if accessors were methods of a corresponding form. Each accessor corresponds to a method with a single value parameter of the event type, a void return type, and the same modifiers as the containing event. 14.9 IndexersAn indexer is a member that enables an object to be indexed in the same way as an array. Indexers are declared using indexer_declarations: indexer_declaration : attributes? indexer_modifier* indexer_declarator indexer_body ; indexer_modifier : 'new' | 'public' | 'protected' | 'internal' | 'private' | 'virtual' | 'sealed' | 'override' | 'abstract' | 'extern' | unsafe_modifier // unsafe code support ; indexer_declarator : type 'this' '[' formal_parameter_list ']' | type interface_type '.' 'this' '[' formal_parameter_list ']' ; indexer_body : '{' accessor_declarations '}' | '=>' expression ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). An indexer_declaration may include a set of attributes (§21) and any one of the permitted kinds of declared accessibility (§14.3.6), the new (§14.3.5), virtual (§14.6.4), override (§14.6.5), sealed (§14.6.6), abstract (§14.6.7), and extern (§14.6.8) modifiers. Indexer declarations are subject to the same rules as method declarations (§14.6) with regard to valid combinations of modifiers, with the one exception being that the static modifier is not permitted on an indexer declaration. The modifiers virtual, override, and abstract are mutually exclusive except in one case. The abstract and override modifiers may be used together so that an abstract indexer can override a virtual one. The type of an indexer declaration specifies the element type of the indexer introduced by the declaration.
Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. For an explicit interface member implementation, the type is followed by an interface_type, a “.”, and the keyword this. Unlike other members, indexers do not have user-defined names. The formal_parameter_list specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method (§14.6.2), except that at least one parameter shall be specified, and that the this, ref, and out parameter modifiers are not permitted. The type of an indexer and each of the types referenced in the formal_parameter_list shall be at least as accessible as the indexer itself (§7.5.5). An indexer_body may either consist of an accessor body (§14.7.1) or an expression body (§14.6.1). In an accessor body, accessor_declarations, which shall be enclosed in “{” and “}” tokens, declare the accessors (§14.7.3) of the indexer. The accessors specify the executable statements associated with reading and writing indexer elements. Based on the presence or absence of get and set accessors, an indexer is classified as follows:
An expression body consisting of “=>” followed by an expression E and a semicolon is exactly equivalent to the block body { get { return E; } }, and can therefore only be used to specify read-only indexers where the result of the get accessor is given by a single expression. Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as a ref or out argument. The formal_parameter_list of an indexer defines the signature (§7.6) of the indexer. Specifically, the signature of an indexer consists of the number and types of its formal parameters. The element type and names of the formal parameters are not part of an indexer’s signature. The signature of an indexer shall differ from the signatures of all other indexers declared in the same class. Indexers and properties are very similar in concept, but differ in the following ways:
Aside from these differences, all rules defined in §14.7.3 and §14.7.4 apply to indexer accessors as well as to property accessors. When an indexer declaration includes an extern modifier, the indexer is said to be an external indexer. Because an external indexer declaration provides no actual implementation, each of its accessor_declarations consists of a semicolon.
14.10 Operators14.10.1 GeneralAn operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. Operators are declared using operator_declarations: operator_declaration : attributes? operator_modifier+ operator_declarator operator_body ; operator_modifier : 'public' | 'static' | 'extern' | unsafe_modifier // unsafe code support ; operator_declarator : unary_operator_declarator | binary_operator_declarator | conversion_operator_declarator ; unary_operator_declarator : type 'operator' overloadable_unary_operator '(' fixed_parameter ')' ; overloadable_unary_operator : '+' | '-' | '!' | '~' | '++' | '--' | 'true' | 'false' ; binary_operator_declarator : type 'operator' overloadable_binary_operator '(' fixed_parameter ',' fixed_parameter ')' ; overloadable_binary_operator : '+' | '-' | '*' | '/' | '%' | '&' | '|' | '^' | '<<' | right_shift | '==' | '!=' | '>' | '<' | '>=' | '<=' ; conversion_operator_declarator : 'implicit' 'operator' type '(' fixed_parameter ')' | 'explicit' 'operator' type '(' fixed_parameter ')' ; operator_body : block | '=>' expression ';' | ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). There are three categories of overloadable operators: Unary operators (§14.10.2), binary operators (§14.10.3), and conversion operators (§14.10.4). The operator_body is either a semicolon, a block body (§14.6.1) or an expression body (§14.6.1). A block body consists of a block, which specifies the statements to execute when the operator is invoked. The block shall conform to the rules for value-returning methods described in §14.6.11. An expression body consists of => followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked. For extern operators, the operator_body consists simply of a semicolon. For all other operators, the operator_body is either a block body or an expression body. The following rules apply to all operator declarations:
Each operator category imposes additional restrictions, as described in the following subclauses. Like other members, operators declared in a base class are inherited by derived classes. Because operator declarations always require the class or struct in which the operator is declared to participate in the signature of the operator, it is not possible for an operator declared in a derived class to hide an operator declared in a base class. Thus, the new modifier is never required, and therefore never permitted, in an operator declaration. Additional information on unary and binary operators can be found in §11.4. Additional information on conversion operators can be found in §10.5. 14.10.2 Unary operatorsThe following rules apply to unary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:
The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. The return type is not part of a unary operator’s signature, nor is the name of the formal parameter. The true and false unary operators require pair-wise declaration. A compile-time error occurs if a class declares one of these operators without also declaring the other. The true and false operators are described further in §11.21.
14.10.3 Binary operatorsThe following rules apply to binary operator declarations, where T denotes the instance type of the class or struct that contains the operator declaration:
Certain binary operators require pair-wise declaration. For every declaration of either operator of a pair, there shall be a matching declaration of the other operator of the pair. Two operator declarations match if identity conversions exist between their return types and their corresponding parameter types. The following operators require pair-wise declaration:
14.10.4 Conversion operatorsA conversion operator declaration introduces a user-defined conversion (§10.5), which augments the pre-defined implicit and explicit conversions. A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. This is described further in §10.2. A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explicit conversions can occur in cast expressions, and are described further in §10.3. A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator. For a given source type S and target type T, if S or T are nullable value types, let S₀ and T₀ refer to their underlying types; otherwise, S₀ and T₀ are equal to S and T respectively. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:
For the purposes of these rules, any type parameters associated with S or T are considered to be unique types that have no inheritance relationship with other types, and any constraints on those type parameters are ignored.
From the second rule, it follows that a conversion operator shall convert either to or from the class or struct type in which the operator is declared.
It is not possible to directly redefine a pre-defined conversion. Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types. Likewise, neither the source nor the target types of a conversion can be a base type of the other, since a conversion would then already exist. However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions.
In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored. Specifically:
For all types but object, the operators declared by the Convertible<T> type above do not conflict with pre-defined conversions.
User-defined conversions are not allowed to convert from or to interface_types. In particular, this restriction ensures that no user-defined transformations occur when converting to an interface_type, and that a conversion to an interface_type succeeds only if the object being converted actually implements the specified interface_type. The signature of a conversion operator consists of the source type and the target type. (This is the only form of member for which the return type participates in the signature.) The implicit or explicit classification of a conversion operator is not part of the operator’s signature. Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.
14.11 Instance constructors14.11.1 GeneralAn instance constructor is a member that implements the actions required to initialize an instance of a class. Instance constructors are declared using constructor_declarations: constructor_declaration : attributes? constructor_modifier* constructor_declarator constructor_body ; constructor_modifier : 'public' | 'protected' | 'internal' | 'private' | 'extern' | unsafe_modifier // unsafe code support ; constructor_declarator : identifier '(' formal_parameter_list? ')' constructor_initializer? ; constructor_initializer : ':' 'base' '(' argument_list? ')' | ':' 'this' '(' argument_list? ')' ; constructor_body : block | '=>' expression ';' | ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). A constructor_declaration may include a set of attributes (§21), any one of the permitted kinds of declared accessibility (§14.3.6), and an extern (§14.6.8) modifier. A constructor declaration is not permitted to include the same modifier multiple times. The identifier of a constructor_declarator shall name the class in which the instance constructor is declared. If any other name is specified, a compile-time error occurs. The optional formal_parameter_list of an instance constructor is subject to the same rules as the formal_parameter_list of a method (§14.6). As the this modifier for parameters only applies to extension methods (§14.6.10), no parameter in a constructor’s formal_parameter_list shall contain the this modifier. The formal parameter list defines the signature (§7.6) of an instance constructor and governs the process whereby overload resolution (§11.6.4) selects a particular instance constructor in an invocation. Each of the types referenced in the formal_parameter_list of an instance constructor shall be at least as accessible as the constructor itself (§7.5.5). The optional constructor_initializer specifies another instance constructor to invoke before executing the statements given in the constructor_body of this instance constructor. This is described further in §14.11.2. When a constructor declaration includes an extern modifier, the constructor is said to be an external constructor. Because an external constructor declaration provides no actual implementation, its constructor_body consists of a semicolon. For all other constructors, the constructor_body consists of either
A constructor_body that is a block or expression body corresponds exactly to the block of an instance method with a void return type (§14.6.11). Instance constructors are not inherited. Thus, a class has no instance constructors other than those actually declared in the class, with the exception that if a class contains no instance constructor declarations, a default instance constructor is automatically provided (§14.11.5). Instance constructors are invoked by object_creation_expressions (§11.7.15.2) and through constructor_initializers. 14.11.2 Constructor initializersAll instance constructors (except those for class object) implicitly include an invocation of another instance constructor immediately before the constructor_body. The constructor to implicitly invoke is determined by the constructor_initializer:
If an instance constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided.
The scope of the parameters given by the formal_parameter_list of an instance constructor declaration includes the constructor initializer of that declaration. Thus, a constructor initializer is permitted to access the parameters of the constructor.
An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as it is a compile-time error for an argument expression to reference any instance member through a simple_name. 14.11.3 Instance variable initializersWhen an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable_initializers of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration (§14.5.6). 14.11.4 Constructor executionVariable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed.
14.11.5 Default constructorsIf a class contains no instance constructor declarations, a default instance constructor is automatically provided. That default constructor simply invokes a constructor of the direct base class, as if it had a constructor initializer of the form base(). If the class is abstract then the declared accessibility for the default constructor is protected. Otherwise, the declared accessibility for the default constructor is public.
If overload resolution is unable to determine a unique best candidate for the base-class constructor initializer then a compile-time error occurs.
14.12 Static constructorsA static constructor is a member that implements the actions required to initialize a closed class. Static constructors are declared using static_constructor_declarations: static_constructor_declaration : attributes? static_constructor_modifiers identifier '(' ')' static_constructor_body ; static_constructor_modifiers : 'static' | 'static' 'extern' unsafe_modifier? | 'static' unsafe_modifier 'extern'? | 'extern' 'static' unsafe_modifier? | 'extern' unsafe_modifier 'static' | unsafe_modifier 'static' 'extern'? | unsafe_modifier 'extern' 'static' ; static_constructor_body : block | '=>' expression ';' | ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). A static_constructor_declaration may include a set of attributes (§21) and an extern modifier (§14.6.8). The identifier of a static_constructor_declaration shall name the class in which the static constructor is declared. If any other name is specified, a compile-time error occurs. When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Because an external static constructor declaration provides no actual implementation, its static_constructor_body consists of a semicolon. For all other static constructor declarations, the static_constructor_body consists of either
A static_constructor_body that is a block or expression body corresponds exactly to the method_body of a static method with a void return type (§14.6.11). Static constructors are not inherited, and cannot be called directly. The static constructor for a closed class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
If a class contains the Main method (§7.1) in which execution begins, the static constructor for that class executes before the Main method is called. To initialize a new closed class type, first a new set of static fields (§14.5.2) for that particular closed type is created. Each of the static fields is initialized to its default value (§14.5.5). Next, the static field initializers (§14.5.6.2) are executed for those static fields. Finally, the static constructor is executed.
It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.
Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints (§14.2.5).
14.13 Finalizers
A finalizer is a member that implements the actions required to finalize an instance of a class. A finalizer is declared using a finalizer_declaration: finalizer_declaration : attributes? '~' identifier '(' ')' finalizer_body | attributes? 'extern' unsafe_modifier? '~' identifier '(' ')' finalizer_body | attributes? unsafe_modifier 'extern'? '~' identifier '(' ')' finalizer_body ; finalizer_body : block | '=>' expression ';' | ';' ;unsafe_modifier (§22.2) is only available in unsafe code (§22). A finalizer_declaration may include a set of attributes (§21). The identifier of a finalizer_declarator shall name the class in which the finalizer is declared. If any other name is specified, a compile-time error occurs. When a finalizer declaration includes an extern modifier, the finalizer is said to be an external finalizer. Because an external finalizer declaration provides no actual implementation, its finalizer_body consists of a semicolon. For all other finalizers, the finalizer_body consists of either
A finalizer_body that is a block or expression body corresponds exactly to the method_body of an instance method with a void return type (§14.6.11). Finalizers are not inherited. Thus, a class has no finalizers other than the one that may be declared in that class.
Finalizers are invoked automatically, and cannot be invoked explicitly. An instance becomes eligible for finalization when it is no longer possible for any code to use that instance. Execution of the finalizer for the instance may occur at any time after the instance becomes eligible for finalization (§7.9). When an instance is finalized, the finalizers in that instance’s inheritance chain are called, in order, from most derived to least derived. A finalizer may be executed on any thread. For further discussion of the rules that govern when and how a finalizer is executed, see §7.9.
Finalizers are implemented by overriding the virtual method Finalize on System.Object. C# programs are not permitted to override this method or call it (or overrides of it) directly.
The compiler behaves as if this method, and overrides of it, do not exist at all.
For a discussion of the behavior when an exception is thrown from a finalizer, see §20.4. 14.14 Iterators14.14.1 GeneralA function member (§11.6) implemented using an iterator block (§12.3) is called an iterator. An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces (§14.14.2) or one of the enumerable interfaces (§14.14.3). It may occur as a method_body, operator_body or accessor_body, whereas events, instance constructors, static constructors and finalizers may not be implemented as iterators. When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any ref or out parameters. 14.14.2 Enumerator interfacesThe enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. For the sake of brevity, in this subclause and its siblings these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively. 14.14.3 Enumerable interfacesThe enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. For the sake of brevity, in this subclause and its siblings these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively. 14.14.4 Yield typeAn iterator produces a sequence of values, all of the same type. This type is called the yield type of the iterator.
14.14.5 Enumerator objects14.14.5.1 GeneralWhen a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an enumerator object is created and returned. This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s MoveNext method is invoked. An enumerator object has the following characteristics:
An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (§6.4.3). An enumerator object may implement more interfaces than those specified above. The following subclauses describe the required behavior of the MoveNext, Current, and Dispose members of the IEnumerator and IEnumerator<T> interface implementations provided by an enumerator object. Enumerator objects do not support the IEnumerator.Reset method. Invoking this method causes a System.NotSupportedException to be thrown. 14.14.5.2 The MoveNext methodThe MoveNext method of an enumerator object encapsulates the code of an iterator block. Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked:
When MoveNext executes the iterator block, execution can be interrupted in four ways: By a yield return statement, by a yield break statement, by encountering the end of the iterator block, and by an exception being thrown and propagated out of the iterator block.
14.14.5.3 The Current propertyAn enumerator object’s Current property is affected by yield return statements in the iterator block. When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified. For an iterator with a yield type other than object, the result of accessing Current through the enumerator object’s IEnumerable implementation corresponds to accessing Current through the enumerator object’s IEnumerator<T> implementation and casting the result to object. 14.14.5.4 The Dispose methodThe Dispose method is used to clean up the iteration by bringing the enumerator object to the after state.
14.14.6 Enumerable objects14.14.6.1 GeneralWhen a function member returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an enumerable object is created and returned. The enumerable object’s GetEnumerator method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s MoveNext method is invoked. An enumerable object has the following characteristics:
An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (§6.4.3). An enumerable object may implement more interfaces than those specified above.
14.14.6.2 The GetEnumerator methodAn enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in §14.14.5. 14.15 Async Functions14.15.1 GeneralA method (§14.6) or anonymous function (§11.16) with the async modifier is called an async function. In general, the term async is used to describe any kind of function that has the async modifier. It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters. The return_type of an async method shall be either void or a task type. The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. For the sake of brevity, in this clause these types are referenced as Task and Task<T>, respectively. An async method returning a task type is said to be task-returning. The exact definition of the task types is implementation-defined, but from the language’s point of view, a task type is in one of the states incomplete, succeeded or faulted. A faulted task records a pertinent exception. A succeeded Task<T> records a result of type T. Task types are awaitable, and tasks can therefore be the operands of await expressions (§11.8.8). An async function has the ability to suspend evaluation by means of await expressions (§11.8.8) in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. The resumption delegate is of type System.Action, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The current caller of an async function invocation is the original caller if the function invocation has never been suspended or the most recent caller of the resumption delegate otherwise. 14.15.2 Evaluation of a task-returning async functionInvocation of a task-returning async function causes an instance of the returned task type to be generated. This is called the return task of the async function. The task is initially in an incomplete state. The async function body is then evaluated until it is either suspended (by reaching an await expression) or terminates, at which point control is returned to the caller, along with the return task. When the body of the async function terminates, the return task is moved out of the incomplete state:
14.15.3 Evaluation of a void-returning async functionIf the return type of the async function is void, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current thread’s synchronization context. The exact definition of synchronization context is implementation-dependent, but is a representation of “where” the current thread is running. The synchronization context is notified when evaluation of a void-returning async function commences, completes successfully, or causes an uncaught exception to be thrown. This allows the context to keep track of how many void-returning async functions are running under it, and to decide how to propagate exceptions coming out of them. |