From 1612676d65fb690a32bb8cdf8424c02f4bdc4038 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 07:20:41 -0500 Subject: [PATCH 01/47] Allow interfaces as well --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index a1419bee9..60ae1ca75 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -6814,7 +6814,7 @@ A constant expression shall either have the value `null` or one of the following Only the following constructs are permitted in constant expressions: - Literals (including the `null` literal). -- References to `const` members of class and struct types. +- References to `const` members of class, struct, and interface types. - References to members of enumeration types. - References to local constants. - Parenthesized subexpressions, which are themselves constant expressions. From ce1962bc79db746714d80369c4cf29a54a34739e Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 07:52:54 -0500 Subject: [PATCH 02/47] support default interface function members --- standard/classes.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 6880e0d67..926c8d5ee 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -270,7 +270,11 @@ The base classes of a class are the direct base class and its base classes. In o 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. +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 type within which it is immediately nested (if any). + +When an interface `IB` extends an interface `IA`, it is a compile-time error for `IA` to depend on `IB`. An interface **directly depends on** its direct base interfaces (if any) and **directly depends on** the type within which it is immediately nested (if any). + +Given these definitions, the complete set of types upon which a type depends is the transitive closure of the *directly depends on* relationship. > *Example*: The example > @@ -1532,6 +1536,8 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants +[This subclause covers constant declarations in classes and structs. That text is augmented by information about declaring constants in interfaces (§interface-constants).] + A ***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. ```ANTLR @@ -1626,6 +1632,8 @@ Constant declarations may depend on constants from other programs, but such depe ### 15.5.1 General +[[§14.5](classes.md#145-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§15.4.1](structs.md#1541-general)) and interfaces (§interface-fields).] + A ***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. ```ANTLR @@ -2049,6 +2057,8 @@ A variable initializer for an instance field cannot reference the instance being ### 15.6.1 General +[[§14.6](classes.md#146-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§15.4](structs.md#154-class-and-struct-differences)) and interfaces ([§17.4.2](interfaces.md#1742-interface-methods)).] + A ***method*** is a member that implements a computation or action that can be performed by an object or class. Methods are declared using *method_declaration*s: ```ANTLR @@ -2912,9 +2922,9 @@ When an instance method declaration includes a `sealed` modifier, that method is When 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. +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. (For an abstract interface method, an implementation may also be provided by a derived interface.) 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 ([§15.2.2.2](classes.md#15222-abstract-classes)). +Abstract method declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). > *Example*: In the following code > @@ -2936,7 +2946,7 @@ Abstract method declarations are only permitted in abstract classes ([§15.2.2.2 > } > ``` > -> the `Shape` class defines the abstract notion of a geometrical shape object that can paint itself. The `Paint` method is abstract because there is no meaningful default implementation. The `Ellipse` and `Box` classes are concrete `Shape` implementations. Because these classes are non-abstract, they are required to override the `Paint` method and provide an actual implementation. +> the `Shape` class defines the abstract notion of a geometrical shape object that can paint itself. The `Paint` method is abstract because there is no meaningful fallback implementation for the abstract concept of shape. The `Ellipse` and `Box` classes are concrete `Shape` implementations. Because these classes are non-abstract, they are required to override the `Paint` method and provide an actual implementation. > > *end example* @@ -3283,6 +3293,8 @@ For returns-by-value and returns-by-ref methods the endpoint of the method body ### 15.7.1 General +[[§14.7](classes.md#147-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§15.4](structs.md#154-class-and-struct-differences)) and in interfaces ([§17.4.3](interfaces.md#1743-interface-properties)).] + A ***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, and the name of a customer. 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 or class’s characteristics; furthermore, they permit such characteristics to be computed. Properties are declared using *property_declaration*s: @@ -3417,7 +3429,7 @@ For a ref-valued property the *ref_get_accessor_declaration* consists optional a The use of *accessor_modifier*s is governed by the following restrictions: -- An *accessor_modifier* shall not be used in an interface or in an explicit interface member implementation. +- An *accessor_modifier* shall not be used in an explicit interface member implementation. - The *accessor_modifier* `readonly` is permitted only in a *property_declaration* or *indexer_declaration* that is contained directly by a *struct_declaration* ([§16.4.11](structs.md#16411-properties), [§16.4.13](structs.md#16413-indexers)). - For a property or indexer that has no `override` modifier, an *accessor_modifier* is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors. - For a property or indexer that includes an `override` modifier, an accessor shall match the *accessor_modifier*, if any, of the accessor being overridden. @@ -3926,11 +3938,11 @@ An accessor that is used to implement an interface shall not have an *accessor_m A 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. +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. (For an abstract interface property, an implementation may also be provided by a derived interface.) 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 ([§15.2.2.2](classes.md#15222-abstract-classes)). 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. +Abstract property declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.3](interfaces.md#1843-interface-properties)). 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 shall 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. @@ -4028,6 +4040,8 @@ When a property is declared as an override, any overridden accessors shall be ac ### 15.8.1 General +[[§14.8](classes.md#14-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§17.4.4](interfaces.md#1744-interface-events)).] + An ***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_declaration*s: @@ -4293,11 +4307,11 @@ The differences between static and instance members are discussed further in [§ A 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_declaration*s. +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. (For an abstract interface event, an implementation may also be provided by a derived interface.) Because an accessor for an abstract event declaration provides no actual implementation, it shall not provide *event_accessor_declaration*s. 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 ([§15.2.2.2](classes.md#15222-abstract-classes)). +Abstract event declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.4](interfaces.md#1844-interface-events)). 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. @@ -4313,6 +4327,8 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov ### 15.9.1 General +[This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§15.4](structs.md#154-class-and-struct-differences)) and in interfaces ([§17.4.5](interfaces.md#1745-interface-indexers)).] + An ***indexer*** is a member that enables an object to be indexed in the same way as an array. Indexers are declared using *indexer_declaration*s: ```ANTLR @@ -4542,6 +4558,8 @@ This replacing of property/properties with indexer/indexers when reading [§15.7 ### 15.10.1 General +[[§14.10](classes.md#1410-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators).] + An ***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_declaration*s: ```ANTLR @@ -5099,6 +5117,8 @@ If overload resolution is unable to determine a unique best candidate for the ba ## 15.12 Static constructors +[This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§15.4.10](structs.md#15410-static-constructors)) and in interfaces (§interface-static-constructors).] + A ***static constructor*** is a member that implements the actions required to initialize a closed class. Static constructors are declared using *static_constructor_declaration*s: ```ANTLR From 25243b474e2a8c1584eebb79ef2db4a35a646461 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 08:47:21 -0500 Subject: [PATCH 03/47] support default interface function members --- standard/interfaces.md | 340 ++++++++++++++++++++++++++++++++++------- 1 file changed, 288 insertions(+), 52 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 0ea1090e6..b84f939c0 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,9 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces can contain methods, properties, events, and indexers. The interface itself does not provide implementations for the members that it declares. The interface merely specifies the members that shall be supplied by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§17.4](interfaces.md#174-interface-members). The interface itself may provide ***default implementations*** for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. + +> *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* ## 18.2 Interface declarations @@ -216,22 +218,33 @@ The members of an interface are the members inherited from the base interfaces a ```ANTLR interface_member_declaration - : interface_method_declaration - | interface_property_declaration - | interface_event_declaration - | interface_indexer_declaration + : constant_declaration + | field_declaration + | method_declaration + | property_declaration + | event_declaration + | indexer_declaration + | static_constructor_declaration + | operator_declaration + | type_declaration ; ``` -An interface declaration declares zero or more members. The members of an interface shall be methods, properties, events, or indexers. An interface cannot contain constants, fields, operators, instance constructors, finalizers, or types, nor can an interface contain static members of any kind. +An interface declaration declares zero or more members. The members of an interface shall be constants, fields, methods, properties, events, indexers, operators, constructors, and types, some of which may be instance, others static, as described in the subclauses for each interface member kind. + +All interface members implicitly have public access; however, an explicit access modifier ([§8.5.2](basic-concepts.md#852-declared-accessibility)) is permitted. + +An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A non-virtual function member may be declared using the `sealed` keyword. -All interface members implicitly have public access. It is a compile-time error for interface member declarations to include any modifiers. +A `private` or `sealed` function member of an interface shall have a body. A `private` function member shall not have the modifier `sealed`. + +An explicitly implemented function member shall not have the modifier `sealed`. An *interface_declaration* creates a new declaration space ([§7.3](basic-concepts.md#73-declarations)), and the type parameters and *interface_member_declaration*s immediately contained by the *interface_declaration* introduce new members into this declaration space. The following rules apply to *interface_member_declaration*s: - The name of a type parameter in the *variant_type_parameter_list* of an interface declaration shall differ from the names of all other type parameters in the same *variant_type_parameter_list* and shall differ from the names of all members of the interface. -- The name of a method shall differ from the names of all properties and events declared in the same interface. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same interface, and two methods declared in the same interface shall not have signatures that differ solely by `in`, `out`, and `ref`. -- The name of a property or event shall differ from the names of all other members declared in the same interface. +- The name of a method shall differ from the names of all other kinds of function members declared in the same interface. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same interface, and two methods declared in the same interface may not have signatures that differ solely by `in`, `out`, and `ref`. +- The name of a non-function member shall differ from the names of all other non-function member declared in the same interface. - The signature of an indexer shall differ from the signatures of all other indexers declared in the same interface. The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does cause a compiler to issue a warning. To suppress the warning, the declaration of the derived interface member shall include a `new` modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in [§7.7.2.3](basic-concepts.md#7723-hiding-through-inheritance). @@ -242,16 +255,109 @@ If a `new` modifier is included in a declaration that doesn’t hide an inherite The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. +Consider an interface with a default implementation for a member `M`. As `M` is not part of that interface’s contract, outside that interface or any interface derived from it, that name is not visible. How then can it be accessed? The following code shows how: + +```csharp +interface IA +{ + public int P { get { return 10; } } + public void M() + { + Console.WriteLine("IA.M"); + } +} + +interface IB : IA +{ + public new int P { get { return 20; } } + void IA.M() + { + Console.WriteLine("IB.M"); + } +} + +class C : IB { } + +class Test +{ + public static void Start() + { + C c = new C(); + ((IA)c).M(); // cast needed + Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed + Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed + } +} +``` + +Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Start`, we cannot wrote `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. + +> *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* + +### §interface-constants Interface constants + +[§15.4](classes.md#154-constants) covers constant declarations in classes. When declaring constants in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +*constant_declaration* is described in [§15.4](classes.md#154-constants). + +As a *constant_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* part of the interface’s contract. + +See §interface-static-constructors for information regarding the allocation and initialization of constants. + +> *Note*: See §interface-fields for an example of using various kinds of static members declared within an interface. *end note* + +### §interface-fields Interface fields + +[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. When declaring fields in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +*field_declaration* is described in [§15.5.1](classes.md#1551-general). + +It is a compile-time error for *field_declaration* to declare an instance field. + +As a static *field_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* part of the interface’s contract. + +> *Example*: The following program contains static members of various kinds: +> +> ```csharp +> using System; +> public interface IX +> { +> private const int constant = 100; +> protected static int field; +> +> static IX() +> { +> Console.WriteLine("static members initialized"); +> Console.WriteLine($"constant = {IX.constant}, field = {IX.field}"); +> field = 50; +> Console.WriteLine("static constructor has run"); +> } +> +> public static void Main() +> { +> Console.WriteLine($"constant = {IX.constant}, field = {IX.field}"); +> } +> } +> ``` +> +> The output produced is +> +> ```console +> static members initialized +> constant = 100, field = 0 +> static constructor has run +> constant = 100, field = 50 +> ``` +> +> *end example* + +See §interface-static-constructors for information regarding the allocation and initialization of static fields. + ### 18.4.2 Interface methods -Interface methods are declared using *interface_method_declaration*s: +[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. When declaring methods in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. ```ANTLR -interface_method_declaration - : attributes? 'new'? return_type interface_method_header - | attributes? 'new'? ref_kind ref_return_type interface_method_header - ; - interface_method_header : identifier '(' parameter_list? ')' ';' | identifier type_parameter_list '(' parameter_list? ')' @@ -259,7 +365,19 @@ interface_method_header ; ``` -The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon. +The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). + +*method_modifier* shall not include `override`. + +An interface method declaration that has a *block* as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. An *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. + +The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: + +- A static declaration that is not extern or abstract shall have a *block* as a *method_body*. +- A virtual declaration that is not extern shall have a *block* as a *method_body*. +- A private declaration that is not extern shall have a *block* as a *method_body*. +- A sealed declaration that is not extern shall have a *block* as a *method_body*. +- An async declaration shall have a *block* as a *method_body*. All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference parameter types shall also be output-safe. @@ -306,51 +424,125 @@ These rules ensure that any covariant or contravariant usage of the interface re > > *end example* -### 18.4.3 Interface properties +> *Note*: See §interface-fields for an example that not only shows a static method with default implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point! *end note* -Interface properties are declared using *interface_property_declaration*s: +A virtual method with implementation declared in an interface may be overridden to be abstract in a derived interface. This is known as ***reabstraction***. -```ANTLR -interface_property_declaration - : attributes? 'new'? type identifier '{' interface_accessors '}' - | attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}' - ; +> *Example*: +> +> ```csharp +> interface IA +> { +> void M() { Console.WriteLine("IA.M"); } +> } +> +> interface IB : IA +> { +> abstract void IA.M(); // reabstraction of M +> } +> ``` +> +> This is useful in derived interfaces where the default implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. *end example* -interface_accessors - : attributes? 'get' ';' - | attributes? 'set' ';' - | attributes? 'get' ';' attributes? 'set' ';' - | attributes? 'set' ';' attributes? 'get' ';' - ; +Reabstraction is also permissible in an implementing class. -ref_interface_accessor - : attributes? 'get' ';' - ; -``` +> *Example*: +> +> ```csharp +> interface I1 +> { +> void M() { } +> } +> +> abstract class C : I1 +> { +> public abstract void M(); // implement I1.M with an abstract method in C +> } +> ``` +> +> *end example* + +Every interface and class shall have a most specific override for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. -The *attributes*, *type*, and *identifier* of an interface property declaration have the same meaning as those of a property declaration in a class ([§15.7](classes.md#157-properties)). +One override `M1` is considered *more specific* than another override `M2` if `M1` is declared on type `T1`, `M2` is declared on type `T2`, and either -The accessors of an interface property declaration correspond to the accessors of a class property declaration ([§15.7.3](classes.md#1573-accessors)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only. +1. `T1` contains `T2` among its direct or indirect interfaces, or +1. `T2` is an interface type but `T1` is not an interface type. + +> *Example*: +> +> ```csharp +> interface IA +> { +> void M() { Console.WriteLine("IA.M"); } +> } +> +> interface IB : IA +> { +> void IA.M() { Console.WriteLine("IB.M"); } +> } +> +> interface IC : IA +> { +> void IA.M() { Console.WriteLine("IC.M"); } +> } +> +> abstract class C : IB, IC { } // error: no most specific override for 'IA.M' +> +> abstract class D : IA, IB, IC // OK +> { +> public abstract void M(); +> } +> ``` +> +> The most specific override rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* + +It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in an interface. + +> *Example*: +> +> ```csharp +> interface IF +> { +> void M(); +> } +> +> abstract class F : IF { } // error: 'F' does not implement 'IF.M' +> ``` +> +> *end example* + +### 18.4.3 Interface properties + +[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. When declaring properties in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +Interface properties are declared using *property_declaration*s ([§15.7.1](classes.md#1571-general)). + +*property_modifier* shall not include `override`. + +A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. A *property_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). + +> *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. ### 18.4.4 Interface events -Interface events are declared using *interface_event_declaration*s: +[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. When declaring events in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. -```ANTLR -interface_event_declaration - : attributes? 'new'? 'event' type identifier ';' - ; -``` +Interface events are declared using *event_declaration*s ([§15.8.1](classes.md#1581-general)). + +*event_modifier* shall not include `override`. + +An *event_declaration* that has an *event_accessor_declarations* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. An *event_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). -The *attributes*, *type*, and *identifier* of an interface event declaration have the same meaning as those of an event declaration in a class ([§15.8](classes.md#158-events)). +It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. The type of an interface event shall be input-safe. ### 18.4.5 Interface indexers -Interface indexers are declared using *interface_indexer_declaration*s: +[§15.9](classes.md#159-indexers) and its subclauses cover index declarations in classes. When declaring indexers in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. ```ANTLR interface_indexer_declaration @@ -361,9 +553,11 @@ interface_indexer_declaration ; ``` -The *attributes*, *type*, and *parameter_list* of an interface indexer declaration have the same meaning as those of an indexer declaration in a class ([§15.9](classes.md#159-indexers)). +Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)). -The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([§15.9](classes.md#159-indexers)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only. +*indexer_modifier* shall not include `override`. + +An *indexer_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference parameter types shall also be output-safe. @@ -371,9 +565,51 @@ All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2 The type of an interface indexer shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. +### §interface-operators Interface operators + +[§14.10](classes.md#1410-operators) and its subclauses cover operator declarations in classes and structs. When declaring operators in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +An *operator_declaration* is a default implementation ([§17.1](interfaces.md#171-general)), so it is *not* part of the interface’s contract. + +It is a compile-time error for an interface to contain a conversion, equality, or inequality operator. + +### §interface-static-constructors Interface static constructors + +[§14.12](classes.md#1412-static-constructors) covers static constructor declarations in classes. When declaring static constructors in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§17.1](interfaces.md#171-general)), so it is *not* part of the interface’s contract. + +The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: + +- Any of the static members of the interface are referenced. +- Before the Main method is called for an interface containing the Main method ([§8.1](basic-concepts.md#81-application-startup)) in which execution begins. + +To initialize a new closed interface type, first a new set of static fields for that particular closed type is created. Each of the static fields is initialized to its default value. Next, the static field initializers are executed for those static fields. Finally, the static constructor is executed. + +> *Note*: See §interface-fields for an example of using various kinds of static members (including a Main method) declared within an interface. *end note* + +### §interface-nested-types Interface nested types + +[§14.3.9](classes.md#1439-nested-types) covers nested types in classes and structs. When declaring nested types in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. + +It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§17.2.3.1](interfaces.md#17231-general)). + +> *Example*: The declaration of `C` below is an error. +> +> ```csharp +> interface IOuter +> { +> class C { } // error: class declaration within scope of variant type parameter 'T' +> } +> ``` +> +> *end example* + ### 18.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. + +### 17.4.6 Interface member access For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. @@ -601,7 +837,7 @@ The base interfaces of a generic class declaration shall satisfy the uniqueness ### 18.6.2 Explicit interface member implementations -For purposes of implementing interfaces, a class or struct may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. +For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. > *Example*: > @@ -655,7 +891,7 @@ For purposes of implementing interfaces, a class or struct may declare ***explic > > *end example* -It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. +It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit instance interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. An explicit interface static member implementation can only be accessed through the interface name. It is a compile-time error for an explicit interface member implementation to include any modifiers ([§15.6](classes.md#156-methods)) other than `extern` or `async`. @@ -692,11 +928,11 @@ A *type_parameter_constraints_clause* on an explicit interface method implementa > Explicit interface member implementations serve two primary purposes: > > - Because explicit interface member implementations are not accessible through class or struct instances, they allow interface implementations to be excluded from the public interface of a class or struct. This is particularly useful when a class or struct implements an internal interface that is of no interest to a consumer of that class or struct. -> - Explicit interface member implementations allow disambiguation of interface members with the same signature. Without explicit interface member implementations it would be impossible for a class or struct to have different implementations of interface members with the same signature and return type, as would it be impossible for a class or struct to have any implementation at all of interface members with the same signature but with different return types. +> - Explicit interface member implementations allow disambiguation of interface members with the same signature. Without explicit interface member implementations it would be impossible for a class, struct, or interface to have different implementations of interface members with the same signature and return type, as would it be impossible for a class, struct, or interface to have any implementation at all of interface members with the same signature but with different return types. > > *end note* -For an explicit interface member implementation to be valid, the class or struct shall name an interface in its base class list that contains a member whose qualified interface member name, type, number of type parameters, and parameter types exactly match those of the explicit interface member implementation. If an interface function member has a parameter array, the corresponding parameter of an associated explicit interface member implementation is allowed, but not required, to have the `params` modifier. If the interface function member does not have a parameter array then an associated explicit interface member implementation shall not have a parameter array. +For an explicit interface member implementation to be valid, the class, struct, or interface shall name an interface in its base class or base interface list that contains a member whose qualified interface member name, type, number of type parameters, and parameter types exactly match those of the explicit interface member implementation. If an interface function member has a parameter array, the corresponding parameter of an associated explicit interface member implementation is allowed, but not required, to have the `params` modifier. If the interface function member does not have a parameter array then an associated explicit interface member implementation shall not have a parameter array. > *Example*: Thus, in the following class > @@ -872,7 +1108,7 @@ When a generic method implicitly implements an interface method, the constraints ### 18.6.5 Interface mapping -A class or struct shall provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. +A class or struct shall provide implementations of all contract members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. Interface mapping for a class or struct `C` locates an implementation for each member of each interface specified in the base class list of `C`. The implementation of a particular interface member `I.M`, where `I` is the interface in which the member `M` is declared, is determined by examining each class or struct `S`, starting with `C` and repeating for each successive base class of `C`, until a match is located: @@ -1269,7 +1505,7 @@ When a class implements an interface, it implicitly also implements all that int ### 18.6.8 Abstract classes and interfaces -Like a non-abstract class, an abstract class shall provide implementations of all members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. +Like a non-abstract class, an abstract class shall provide implementations of all contract members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. > *Example*: > From 75567df60337df35f903ebbddfcf6486afbca986 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 09:02:46 -0500 Subject: [PATCH 04/47] Update classes.md --- standard/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/classes.md b/standard/classes.md index 926c8d5ee..4cf6aa233 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -4040,7 +4040,7 @@ When a property is declared as an override, any overridden accessors shall be ac ### 15.8.1 General -[[§14.8](classes.md#14-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§17.4.4](interfaces.md#1744-interface-events)).] +[[§14.8](classes.md#148-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§17.4.4](interfaces.md#1744-interface-events)).] An ***event*** is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying ***event handlers***. From 3ce3ccd9c3dd4906b49b19b10ddb2c68a3972f38 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 09:06:39 -0500 Subject: [PATCH 05/47] Update interfaces.md --- standard/interfaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index b84f939c0..92e492ae8 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -232,7 +232,7 @@ interface_member_declaration An interface declaration declares zero or more members. The members of an interface shall be constants, fields, methods, properties, events, indexers, operators, constructors, and types, some of which may be instance, others static, as described in the subclauses for each interface member kind. -All interface members implicitly have public access; however, an explicit access modifier ([§8.5.2](basic-concepts.md#852-declared-accessibility)) is permitted. +All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted. An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A non-virtual function member may be declared using the `sealed` keyword. @@ -582,7 +582,7 @@ A *static_constructor_declaration* that has a *block* as a *static_constructor_b The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: - Any of the static members of the interface are referenced. -- Before the Main method is called for an interface containing the Main method ([§8.1](basic-concepts.md#81-application-startup)) in which execution begins. +- Before the Main method is called for an interface containing the Main method ([§7.1](basic-concepts.md#71-application-startup)) in which execution begins. To initialize a new closed interface type, first a new set of static fields for that particular closed type is created. Each of the static fields is initialized to its default value. Next, the static field initializers are executed for those static fields. Finally, the static constructor is executed. From ed7d7c3f33856d28aba470503a3008ec84c76d33 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Mon, 5 Dec 2022 09:15:16 -0500 Subject: [PATCH 06/47] Update interfaces.md --- standard/interfaces.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 92e492ae8..d7d5a18cc 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -373,11 +373,11 @@ An interface method declaration that has a *block* as a *method_body* is a defau The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: -- A static declaration that is not extern or abstract shall have a *block* as a *method_body*. -- A virtual declaration that is not extern shall have a *block* as a *method_body*. -- A private declaration that is not extern shall have a *block* as a *method_body*. -- A sealed declaration that is not extern shall have a *block* as a *method_body*. -- An async declaration shall have a *block* as a *method_body*. +- A static declaration that is not extern or abstract shall have a *block* as a *method_body*. +- A virtual declaration that is not extern shall have a *block* as a *method_body*. +- A private declaration that is not extern shall have a *block* as a *method_body*. +- A sealed declaration that is not extern shall have a *block* as a *method_body*. +- An async declaration shall have a *block* as a *method_body*. All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference parameter types shall also be output-safe. @@ -423,7 +423,9 @@ These rules ensure that any covariant or contravariant usage of the interface re > This is actually a call to `C.M`. But that call requires that `E` derive from `D`, so type safety would be violated here. > > *end example* + + > *Note*: See §interface-fields for an example that not only shows a static method with default implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point! *end note* A virtual method with implementation declared in an interface may be overridden to be abstract in a derived interface. This is known as ***reabstraction***. From 3086454916272c664836b1b2516877d92fae31e2 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 22 Jan 2023 15:30:57 -0500 Subject: [PATCH 07/47] add annotation to new examples --- standard/interfaces.md | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index d7d5a18cc..b8a3b8aa4 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -257,6 +257,7 @@ The set of members of an interface declared in multiple parts ([§15.2.7](classe Consider an interface with a default implementation for a member `M`. As `M` is not part of that interface’s contract, outside that interface or any interface derived from it, that name is not visible. How then can it be accessed? The following code shows how: + ```csharp interface IA { @@ -267,7 +268,7 @@ interface IA } } -interface IB : IA +interface IB: IA { public new int P { get { return 20; } } void IA.M() @@ -276,11 +277,11 @@ interface IB : IA } } -class C : IB { } +class C: IB { } class Test { - public static void Start() + public static void Main() { C c = new C(); ((IA)c).M(); // cast needed @@ -318,11 +319,11 @@ As a static *field_declaration* is considered to have a default implementation ( > *Example*: The following program contains static members of various kinds: > +> > ```csharp -> using System; > public interface IX > { -> private const int constant = 100; +> public const int constant = 100; > protected static int field; > > static IX() @@ -332,7 +333,10 @@ As a static *field_declaration* is considered to have a default implementation ( > field = 50; > Console.WriteLine("static constructor has run"); > } -> +> } +> +> public class Test: IX +> { > public static void Main() > { > Console.WriteLine($"constant = {IX.constant}, field = {IX.field}"); @@ -407,9 +411,9 @@ These rules ensure that any covariant or contravariant usage of the interface re > > ```csharp > class B {} -> class D : B {} -> class E : B {} -> class C : I +> class D: B {} +> class E: B {} +> class C: I > { > public void M() {...} > } @@ -432,13 +436,14 @@ A virtual method with implementation declared in an interface may be overridden > *Example*: > +> > ```csharp > interface IA > { > void M() { Console.WriteLine("IA.M"); } > } > -> interface IB : IA +> interface IB: IA > { > abstract void IA.M(); // reabstraction of M > } @@ -450,13 +455,14 @@ Reabstraction is also permissible in an implementing class. > *Example*: > +> > ```csharp > interface I1 > { > void M() { } > } > -> abstract class C : I1 +> abstract class C: I1 > { > public abstract void M(); // implement I1.M with an abstract method in C > } @@ -473,6 +479,7 @@ One override `M1` is considered *more specific* than another override `M2` if `M > *Example*: > +> > ```csharp > interface IA > { @@ -484,14 +491,14 @@ One override `M1` is considered *more specific* than another override `M2` if `M > void IA.M() { Console.WriteLine("IB.M"); } > } > -> interface IC : IA +> interface IC: IA > { > void IA.M() { Console.WriteLine("IC.M"); } > } > -> abstract class C : IB, IC { } // error: no most specific override for 'IA.M' +> abstract class C: IB, IC { } // error: no most specific override for 'IA.M' > -> abstract class D : IA, IB, IC // OK +> abstract class D: IA, IB, IC // OK > { > public abstract void M(); > } @@ -503,6 +510,7 @@ It is an error if in a class declaration the most specific override of some inte > *Example*: > +> > ```csharp > interface IF > { @@ -598,6 +606,7 @@ It is an error to declare a class type, struct type, or enum type within the sco > *Example*: The declaration of `C` below is an error. > +> > ```csharp > interface IOuter > { From 1b8a84eb9664fd6da2c63b48b78151b382553a64 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 25 Sep 2023 17:14:21 -0400 Subject: [PATCH 08/47] first pass at fixing references --- standard/classes.md | 12 ++++++------ standard/interfaces.md | 14 ++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 4cf6aa233..0fbd5599f 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1632,7 +1632,7 @@ Constant declarations may depend on constants from other programs, but such depe ### 15.5.1 General -[[§14.5](classes.md#145-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§15.4.1](structs.md#1541-general)) and interfaces (§interface-fields).] +[[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§16.4.1](structs.md#1641-general)) and interfaces (§interface-fields).] A ***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. @@ -2057,7 +2057,7 @@ A variable initializer for an instance field cannot reference the instance being ### 15.6.1 General -[[§14.6](classes.md#146-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§15.4](structs.md#154-class-and-struct-differences)) and interfaces ([§17.4.2](interfaces.md#1742-interface-methods)).] +[[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§16.4](structs.md#164-class-and-struct-differences)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)).] A ***method*** is a member that implements a computation or action that can be performed by an object or class. Methods are declared using *method_declaration*s: @@ -3293,7 +3293,7 @@ For returns-by-value and returns-by-ref methods the endpoint of the method body ### 15.7.1 General -[[§14.7](classes.md#147-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§15.4](structs.md#154-class-and-struct-differences)) and in interfaces ([§17.4.3](interfaces.md#1743-interface-properties)).] +[[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.3](interfaces.md#1843-interface-properties)).] A ***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, and the name of a customer. 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 or class’s characteristics; furthermore, they permit such characteristics to be computed. @@ -4040,7 +4040,7 @@ When a property is declared as an override, any overridden accessors shall be ac ### 15.8.1 General -[[§14.8](classes.md#148-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§17.4.4](interfaces.md#1744-interface-events)).] +[[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§18.4.4](interfaces.md#1844-interface-events)).] An ***event*** is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying ***event handlers***. @@ -4327,7 +4327,7 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov ### 15.9.1 General -[This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§15.4](structs.md#154-class-and-struct-differences)) and in interfaces ([§17.4.5](interfaces.md#1745-interface-indexers)).] +[This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.5](interfaces.md#1845-interface-indexers)).] An ***indexer*** is a member that enables an object to be indexed in the same way as an array. Indexers are declared using *indexer_declaration*s: @@ -4558,7 +4558,7 @@ This replacing of property/properties with indexer/indexers when reading [§15.7 ### 15.10.1 General -[[§14.10](classes.md#1410-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators).] +[[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators).] An ***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_declaration*s: diff --git a/standard/interfaces.md b/standard/interfaces.md index b8a3b8aa4..723077600 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -577,17 +577,17 @@ The type of an interface indexer shall be output-safe if there is a get accessor ### §interface-operators Interface operators -[§14.10](classes.md#1410-operators) and its subclauses cover operator declarations in classes and structs. When declaring operators in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. When declaring operators in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. -An *operator_declaration* is a default implementation ([§17.1](interfaces.md#171-general)), so it is *not* part of the interface’s contract. +An *operator_declaration* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. It is a compile-time error for an interface to contain a conversion, equality, or inequality operator. ### §interface-static-constructors Interface static constructors -[§14.12](classes.md#1412-static-constructors) covers static constructor declarations in classes. When declaring static constructors in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +[§15.12](classes.md#1512-static-constructors) covers static constructor declarations in classes. When declaring static constructors in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. -A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§17.1](interfaces.md#171-general)), so it is *not* part of the interface’s contract. +A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: @@ -600,9 +600,9 @@ To initialize a new closed interface type, first a new set of static fields for ### §interface-nested-types Interface nested types -[§14.3.9](classes.md#1439-nested-types) covers nested types in classes and structs. When declaring nested types in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +[§15.3.9](classes.md#1539-nested-types) covers nested types in classes and structs. When declaring nested types in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. -It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§17.2.3.1](interfaces.md#17231-general)). +It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§18.2.3.1](interfaces.md#18231-general)). > *Example*: The declaration of `C` below is an error. > @@ -620,8 +620,6 @@ It is an error to declare a class type, struct type, or enum type within the sco Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. -### 17.4.6 Interface member access - For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. > *Example*: In the following code From e056252ca76e78a93379e30eeb8977081e06116f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 25 Sep 2023 17:31:00 -0400 Subject: [PATCH 09/47] find link errors part 2 --- standard/classes.md | 2 +- standard/interfaces.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 0fbd5599f..71f3f4095 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -5117,7 +5117,7 @@ If overload resolution is unable to determine a unique best candidate for the ba ## 15.12 Static constructors -[This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§15.4.10](structs.md#15410-static-constructors)) and in interfaces (§interface-static-constructors).] +[This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§16.4.10](structs.md#16410-static-constructors)) and in interfaces (§interface-static-constructors).] A ***static constructor*** is a member that implements the actions required to initialize a closed class. Static constructors are declared using *static_constructor_declaration*s: diff --git a/standard/interfaces.md b/standard/interfaces.md index 723077600..2b0a63fba 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§17.4](interfaces.md#174-interface-members). The interface itself may provide ***default implementations*** for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementations*** for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* From edcbd60c0e7f206e3bf2863df80d1efcf5b3a8eb Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 25 Sep 2023 17:37:55 -0400 Subject: [PATCH 10/47] word converter warnings Enclosing a paragraph in brackets breaks the word converter. It looks for a link. --- standard/classes.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 71f3f4095..185186fdc 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1632,7 +1632,7 @@ Constant declarations may depend on constants from other programs, but such depe ### 15.5.1 General -[[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§16.4.1](structs.md#1641-general)) and interfaces (§interface-fields).] +[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§16.4.1](structs.md#1641-general)) and interfaces (§interface-fields). A ***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. @@ -2057,7 +2057,7 @@ A variable initializer for an instance field cannot reference the instance being ### 15.6.1 General -[[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§16.4](structs.md#164-class-and-struct-differences)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)).] +[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§16.4](structs.md#164-class-and-struct-differences)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). A ***method*** is a member that implements a computation or action that can be performed by an object or class. Methods are declared using *method_declaration*s: @@ -3293,7 +3293,7 @@ For returns-by-value and returns-by-ref methods the endpoint of the method body ### 15.7.1 General -[[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.3](interfaces.md#1843-interface-properties)).] +[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.3](interfaces.md#1843-interface-properties)). A ***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, and the name of a customer. 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 or class’s characteristics; furthermore, they permit such characteristics to be computed. @@ -4040,7 +4040,7 @@ When a property is declared as an override, any overridden accessors shall be ac ### 15.8.1 General -[[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§18.4.4](interfaces.md#1844-interface-events)).] +[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§18.4.4](interfaces.md#1844-interface-events)). An ***event*** is a member that enables an object or class to provide notifications. Clients can attach executable code for events by supplying ***event handlers***. @@ -4327,7 +4327,7 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov ### 15.9.1 General -[This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.5](interfaces.md#1845-interface-indexers)).] +This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.5](interfaces.md#1845-interface-indexers)). An ***indexer*** is a member that enables an object to be indexed in the same way as an array. Indexers are declared using *indexer_declaration*s: @@ -4558,7 +4558,7 @@ This replacing of property/properties with indexer/indexers when reading [§15.7 ### 15.10.1 General -[[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators).] +[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators). An ***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_declaration*s: @@ -5117,7 +5117,7 @@ If overload resolution is unable to determine a unique best candidate for the ba ## 15.12 Static constructors -[This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§16.4.10](structs.md#16410-static-constructors)) and in interfaces (§interface-static-constructors).] +This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§16.4.10](structs.md#16410-static-constructors)) and in interfaces (§interface-static-constructors). A ***static constructor*** is a member that implements the actions required to initialize a closed class. Static constructors are declared using *static_constructor_declaration*s: From 8fad8fa384b83effa0dce3b45e9991bb2559217e Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 25 Aug 2024 15:40:42 -0400 Subject: [PATCH 11/47] minor tweaks --- standard/classes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/classes.md b/standard/classes.md index 185186fdc..1bcc8a5b4 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1536,7 +1536,7 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants -[This subclause covers constant declarations in classes and structs. That text is augmented by information about declaring constants in interfaces (§interface-constants).] +This subclause covers constant declarations in classes and structs. That text is augmented by information about declaring constants in interfaces (§interface-constants). A ***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. From d54fd4c1f89635d29b0844e4bb7628650f4cda15 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Tue, 12 Nov 2024 16:25:46 -0500 Subject: [PATCH 12/47] Fix editorial nits --- standard/interfaces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 2b0a63fba..7e93dc29f 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementations*** for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* @@ -291,7 +291,7 @@ class Test } ``` -Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Start`, we cannot wrote `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. +Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Start`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. > *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* @@ -373,7 +373,7 @@ The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter *method_modifier* shall not include `override`. -An interface method declaration that has a *block* as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. An *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. +An interface method declaration that has a *block* as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: From 4a3dcf7cc4509b2182f1b9399997c6eaac1640c2 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 28 May 2025 11:22:50 -0400 Subject: [PATCH 13/47] fix link --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 7e93dc29f..225ced9ce 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -618,7 +618,7 @@ It is an error to declare a class type, struct type, or enum type within the sco ### 18.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128112-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. From a811a3319afa987e7f108ad27764db776b91690b Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 28 May 2025 11:27:36 -0400 Subject: [PATCH 14/47] fix link --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 225ced9ce..7e93dc29f 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -618,7 +618,7 @@ It is an error to declare a class type, struct type, or enum type within the sco ### 18.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128112-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. From f4f700ee729217f068c967cc21ea029de19dd10b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 21 Jul 2025 17:06:22 -0400 Subject: [PATCH 15/47] Move interface descriptions to interfaces. Some of the descriptions of interface members were in the class clause. Move them to the interface clause. Also, instead of adding a link from classes to interfaces and structs into each sub-clause, provide one blanket statement at the beginning of classes.md. --- standard/classes.md | 28 ++++++---------------------- standard/interfaces.md | 8 +++++++- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 1bcc8a5b4..e85a06ae8 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -4,6 +4,8 @@ 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***. +Structs (§16) and interfaces (§18) provide data structures that contain members similar to classes. Rules restrict the declarations for struct and interface types, and the member declarations allowed in struct and interface types. This clause defines the declarations for classes and class members. The clauses for structs and interfaces define the restrictions for those types in terms of the corresponding declarations in class types. + ## 15.2 Class declarations ### 15.2.1 General @@ -272,10 +274,6 @@ Except for class `object`, every class has exactly one direct base class. The `o 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 type within which it is immediately nested (if any). -When an interface `IB` extends an interface `IA`, it is a compile-time error for `IA` to depend on `IB`. An interface **directly depends on** its direct base interfaces (if any) and **directly depends on** the type within which it is immediately nested (if any). - -Given these definitions, the complete set of types upon which a type depends is the transitive closure of the *directly depends on* relationship. - > *Example*: The example > > @@ -1536,8 +1534,6 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants -This subclause covers constant declarations in classes and structs. That text is augmented by information about declaring constants in interfaces (§interface-constants). - A ***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. ```ANTLR @@ -1632,8 +1628,6 @@ Constant declarations may depend on constants from other programs, but such depe ### 15.5.1 General -[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. That text is augmented by information about declaring fields in structs ([§16.4.1](structs.md#1641-general)) and interfaces (§interface-fields). - A ***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. ```ANTLR @@ -2152,6 +2146,7 @@ A declaration has a valid combination of modifiers if all of the following are t - The declaration includes at most one of the following modifiers: `static`, `virtual`, and `override`. - The declaration includes at most one of the following modifiers: `new` and `override`. - If the declaration includes the `abstract` modifier, then the declaration does not include any of the following modifiers: `static`, `virtual`, `sealed`, or `extern`. +- The declaration may include the `abstract` and `override` modifiers so that an abstract member may override a virtual member. - If the declaration includes the `private` modifier, then the declaration does not include any of the following modifiers: `virtual`, `override`, or `abstract`. - If the declaration includes the `sealed` modifier, then the declaration also includes the `override` modifier. - If the declaration includes the `partial` modifier, then it does not include any of the following modifiers: `new`, `public`, `protected`, `internal`, `private`, `virtual`, `sealed`, `override`, `abstract`, or `extern`. @@ -2922,7 +2917,7 @@ When an instance method declaration includes a `sealed` modifier, that method is When 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. (For an abstract interface method, an implementation may also be provided by a derived interface.) Because an abstract method provides no actual implementation, the *method_body* of an abstract method simply consists of a semicolon. +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 ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). @@ -3293,8 +3288,6 @@ For returns-by-value and returns-by-ref methods the endpoint of the method body ### 15.7.1 General -[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. That text is augmented by information about declaring properties in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.3](interfaces.md#1843-interface-properties)). - A ***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, and the name of a customer. 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 or class’s characteristics; furthermore, they permit such characteristics to be computed. Properties are declared using *property_declaration*s: @@ -3429,7 +3422,6 @@ For a ref-valued property the *ref_get_accessor_declaration* consists optional a The use of *accessor_modifier*s is governed by the following restrictions: -- An *accessor_modifier* shall not be used in an explicit interface member implementation. - The *accessor_modifier* `readonly` is permitted only in a *property_declaration* or *indexer_declaration* that is contained directly by a *struct_declaration* ([§16.4.11](structs.md#16411-properties), [§16.4.13](structs.md#16413-indexers)). - For a property or indexer that has no `override` modifier, an *accessor_modifier* is permitted only if the property or indexer has both a get and set accessor, and then is permitted only on one of those accessors. - For a property or indexer that includes an `override` modifier, an accessor shall match the *accessor_modifier*, if any, of the accessor being overridden. @@ -3938,7 +3930,7 @@ An accessor that is used to implement an interface shall not have an *accessor_m A 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. (For an abstract interface property, an implementation may also be provided by a derived interface.) 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. +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. @@ -4040,8 +4032,6 @@ When a property is declared as an override, any overridden accessors shall be ac ### 15.8.1 General -[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. That text is augmented by information about declaring events in interfaces ([§18.4.4](interfaces.md#1844-interface-events)). - An ***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_declaration*s: @@ -4307,7 +4297,7 @@ The differences between static and instance members are discussed further in [§ A 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. (For an abstract interface event, an implementation may also be provided by a derived interface.) Because an accessor for an abstract event declaration provides no actual implementation, it shall not provide *event_accessor_declaration*s. +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_declaration*s. 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. @@ -4327,8 +4317,6 @@ Except for differences in declaration and invocation syntax, virtual, sealed, ov ### 15.9.1 General -This subclause covers indexer declarations in classes. That text is augmented by information about declaring indexers in structs ([§16.4](structs.md#164-class-and-struct-differences)) and in interfaces ([§18.4.5](interfaces.md#1845-interface-indexers)). - An ***indexer*** is a member that enables an object to be indexed in the same way as an array. Indexers are declared using *indexer_declaration*s: ```ANTLR @@ -4558,8 +4546,6 @@ This replacing of property/properties with indexer/indexers when reading [§15.7 ### 15.10.1 General -[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. That text is augmented by information about declaring operators in interfaces (§interface-operators). - An ***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_declaration*s: ```ANTLR @@ -5117,8 +5103,6 @@ If overload resolution is unable to determine a unique best candidate for the ba ## 15.12 Static constructors -This subclause covers static constructor declarations in classes. That text is augmented by information about declaring static constructors in structs ([§16.4.10](structs.md#16410-static-constructors)) and in interfaces (§interface-static-constructors). - A ***static constructor*** is a member that implements the actions required to initialize a closed class. Static constructors are declared using *static_constructor_declaration*s: ```ANTLR diff --git a/standard/interfaces.md b/standard/interfaces.md index 7e93dc29f..f215dd675 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -234,7 +234,7 @@ An interface declaration declares zero or more members. The members of an interf All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted. -An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A non-virtual function member may be declared using the `sealed` keyword. +An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A derived interface may override an abstract member declared in a base interface. A non-virtual function member may be declared using the `sealed` keyword. A `private` or `sealed` function member of an interface shall have a body. A `private` function member shall not have the modifier `sealed`. @@ -530,8 +530,12 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas *property_modifier* shall not include `override`. +An explicit interface member implementation shall not contain an *accessor_modifier* (§15.7.3). + A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. A *property_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +A derived interface mey implement an abstract interface property declared in a base interface. + > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. @@ -544,6 +548,8 @@ Interface events are declared using *event_declaration*s ([§15.8.1](classes.md# *event_modifier* shall not include `override`. +A derived interface may implement an abstract interface event declared in a base interface (§15.8.5). + An *event_declaration* that has an *event_accessor_declarations* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. An *event_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. From 22a41a3bd1278071587c47d52526e86f92f2ed83 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 21 Jul 2025 17:08:04 -0400 Subject: [PATCH 16/47] Apply suggestions from code review Co-authored-by: Joseph Musser --- standard/interfaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index f215dd675..75f508584 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares, in which case, those members are *not* part of the interface contract. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* @@ -377,7 +377,7 @@ An interface method declaration that has a *block* as a *method_body* is a defau The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: -- A static declaration that is not extern or abstract shall have a *block* as a *method_body*. +- A static declaration that is not extern shall have a *block* as a *method_body*. - A virtual declaration that is not extern shall have a *block* as a *method_body*. - A private declaration that is not extern shall have a *block* as a *method_body*. - A sealed declaration that is not extern shall have a *block* as a *method_body*. From a026967ed6b44d6392891b08dafc58ae2731148f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 22 Jul 2025 11:19:15 -0400 Subject: [PATCH 17/47] edit pass on interfaces Clean up the interfaces markdown file --- standard/classes.md | 2 +- standard/interfaces.md | 84 ++++++++++++++++++------------------------ 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index e85a06ae8..27fc755b9 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1534,7 +1534,7 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants -A ***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. +A ***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. A *constant_declaration* may appear in a class, struct, or interface declaration. ```ANTLR constant_declaration diff --git a/standard/interfaces.md b/standard/interfaces.md index 75f508584..743be157c 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares. For those members for which the interface does not provide default implementations, the interface merely specifies the members that shall be supplied by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* @@ -230,9 +230,9 @@ interface_member_declaration ; ``` -An interface declaration declares zero or more members. The members of an interface shall be constants, fields, methods, properties, events, indexers, operators, constructors, and types, some of which may be instance, others static, as described in the subclauses for each interface member kind. +An interface declaration declares zero or more members. The members of an interface shall be constants, fields, methods, properties, events, indexers, operators, constructors, and types, some of which may be instance, others static, as described in the subclauses for each interface member kind. Interfaces shall not declare instance data members, including field like events. -All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted. +All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted except on static constructors (§15.12). An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A derived interface may override an abstract member declared in a base interface. A non-virtual function member may be declared using the `sealed` keyword. @@ -295,27 +295,13 @@ Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. > *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* -### §interface-constants Interface constants - -[§15.4](classes.md#154-constants) covers constant declarations in classes. When declaring constants in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. - -*constant_declaration* is described in [§15.4](classes.md#154-constants). - -As a *constant_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* part of the interface’s contract. - -See §interface-static-constructors for information regarding the allocation and initialization of constants. - -> *Note*: See §interface-fields for an example of using various kinds of static members declared within an interface. *end note* - ### §interface-fields Interface fields -[§15.5](classes.md#155-fields) and its subclauses cover field declarations in classes. When declaring fields in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. - -*field_declaration* is described in [§15.5.1](classes.md#1551-general). +This clause augments the description of fields in classes [§15.5](classes.md#155-fields) for fields declared in interfaces. *field_declaration* is described in [§15.5.1](classes.md#1551-general). It is a compile-time error for *field_declaration* to declare an instance field. -As a static *field_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* part of the interface’s contract. +As a static *field_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* abstract. > *Example*: The following program contains static members of various kinds: > @@ -359,7 +345,7 @@ See §interface-static-constructors for information regarding the allocation and ### 18.4.2 Interface methods -[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. When declaring methods in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of methods in classes [§15.6](classes.md#156-methods) for methods declared in interfaces. ```ANTLR interface_method_header @@ -373,15 +359,15 @@ The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter *method_modifier* shall not include `override`. -An interface method declaration that has a *block* as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. +An interface method declaration that has a block body or expression body as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: -- A static declaration that is not extern shall have a *block* as a *method_body*. -- A virtual declaration that is not extern shall have a *block* as a *method_body*. -- A private declaration that is not extern shall have a *block* as a *method_body*. -- A sealed declaration that is not extern shall have a *block* as a *method_body*. -- An async declaration shall have a *block* as a *method_body*. +- A static declaration that is not extern shall have a block body or expression body as a *method_body*. +- A virtual declaration that is not extern shall have a block body or expression body as a *method_body*. +- A private declaration that is not extern shall have a block body or expression body as a *method_body*. +- A sealed declaration that is not extern shall have a block body or expression body as a *method_body*. +- An async declaration shall have a block body or expression body as a *method_body*. All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference parameter types shall also be output-safe. @@ -389,8 +375,6 @@ All parameter types of an interface method shall be input-safe ([§18.2.3.2](int Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe. -Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameter of the method shall be input-safe. - These rules ensure that any covariant or contravariant usage of the interface remains typesafe. > *Example*: @@ -408,14 +392,17 @@ These rules ensure that any covariant or contravariant usage of the interface re > Were this restriction not in place it would be possible to violate type safety in the following manner: > > -> > ```csharp +> interface I +> { +> void M() where U : T; +> } > class B {} > class D: B {} > class E: B {} > class C: I > { -> public void M() {...} +> public void M() {...} > } > > ... @@ -430,7 +417,7 @@ These rules ensure that any covariant or contravariant usage of the interface re -> *Note*: See §interface-fields for an example that not only shows a static method with default implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point! *end note* +> *Note*: See §interface-fields for an example that not only shows a static method with default implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point. *end note* A virtual method with implementation declared in an interface may be overridden to be abstract in a derived interface. This is known as ***reabstraction***. @@ -470,12 +457,13 @@ Reabstraction is also permissible in an implementing class. > > *end example* -Every interface and class shall have a most specific override for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. + +Every interface and class shall have a most specific implementation for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. One override `M1` is considered *more specific* than another override `M2` if `M1` is declared on type `T1`, `M2` is declared on type `T2`, and either -1. `T1` contains `T2` among its direct or indirect interfaces, or -1. `T2` is an interface type but `T1` is not an interface type. +1. `T2` is an interface type but `T1` is not an interface type or +1. `T1` contains `T2` among its direct or indirect interfaces. > *Example*: > @@ -506,7 +494,7 @@ One override `M1` is considered *more specific* than another override `M2` if `M > > The most specific override rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* -It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in an interface. +It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in a class or an interface. > *Example*: > @@ -524,7 +512,7 @@ It is an error if in a class declaration the most specific override of some inte ### 18.4.3 Interface properties -[§15.7](classes.md#157-properties) and its subclauses cover property declarations in classes. When declaring properties in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of properties in classes [§15.7](classes.md#157-properties) for properties declared in interfaces. Interface properties are declared using *property_declaration*s ([§15.7.1](classes.md#1571-general)). @@ -532,7 +520,7 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas An explicit interface member implementation shall not contain an *accessor_modifier* (§15.7.3). -A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. A *property_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no default implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). A derived interface mey implement an abstract interface property declared in a base interface. @@ -542,7 +530,7 @@ The type of an interface property shall be output-safe if there is a get accesso ### 18.4.4 Interface events -[§15.8](classes.md#158-events) and its subclauses cover event declarations in classes and structs. When declaring events in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of events in classes [§15.8](classes.md#158-events) for events declared in interfaces. Interface events are declared using *event_declaration*s ([§15.8.1](classes.md#1581-general)). @@ -550,7 +538,7 @@ Interface events are declared using *event_declaration*s ([§15.8.1](classes.md# A derived interface may implement an abstract interface event declared in a base interface (§15.8.5). -An *event_declaration* that has an *event_accessor_declarations* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. An *event_declaration* that has no default implementation is always considered part of the interface’s contract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). +An *event_declaration* that has an *event_accessor_declarations* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An *event_declaration* that has no default implementation is always considered abstract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. @@ -558,7 +546,7 @@ The type of an interface event shall be input-safe. ### 18.4.5 Interface indexers -[§15.9](classes.md#159-indexers) and its subclauses cover index declarations in classes. When declaring indexers in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of indexers in classes [§15.9](classes.md#159-indexers) for indexers declared in interfaces. ```ANTLR interface_indexer_declaration @@ -573,7 +561,7 @@ Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.m *indexer_modifier* shall not include `override`. -An *indexer_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. +An *indexer_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference parameter types shall also be output-safe. @@ -583,17 +571,17 @@ The type of an interface indexer shall be output-safe if there is a get accessor ### §interface-operators Interface operators -[§15.10](classes.md#1510-operators) and its subclauses cover operator declarations in classes and structs. When declaring operators in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of *operator_declaration* members in classes [§15.10](classes.md#1510-operators) for operators declared in interfaces. -An *operator_declaration* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. +An *operator_declaration* in an interface is a default implementation ([§18.1](interfaces.md#181-general)). It is a compile-time error for an interface to contain a conversion, equality, or inequality operator. ### §interface-static-constructors Interface static constructors -[§15.12](classes.md#1512-static-constructors) covers static constructor declarations in classes. When declaring static constructors in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of static constructors in classes [§15.12](classes.md#1512-static-constructors) for static constructors declared in interfaces. -A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* part of the interface’s contract. +A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§18.1](interfaces.md#181-general)). The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: @@ -606,7 +594,7 @@ To initialize a new closed interface type, first a new set of static fields for ### §interface-nested-types Interface nested types -[§15.3.9](classes.md#1539-nested-types) covers nested types in classes and structs. When declaring nested types in interfaces, that text needs to be interpreted in the context of an interface, and, where necessary, it is augmented and/or replaced by text in this subclause. +This clause augments the description of nested types in classes [§15.3.9](classes.md#1539-nested-types) for nested types declared in interfaces. It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§18.2.3.1](interfaces.md#18231-general)). @@ -1123,7 +1111,7 @@ When a generic method implicitly implements an interface method, the constraints ### 18.6.5 Interface mapping -A class or struct shall provide implementations of all contract members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. +A class or struct shall provide implementations of all abstract members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. Interface mapping for a class or struct `C` locates an implementation for each member of each interface specified in the base class list of `C`. The implementation of a particular interface member `I.M`, where `I` is the interface in which the member `M` is declared, is determined by examining each class or struct `S`, starting with `C` and repeating for each successive base class of `C`, until a match is located: @@ -1520,7 +1508,7 @@ When a class implements an interface, it implicitly also implements all that int ### 18.6.8 Abstract classes and interfaces -Like a non-abstract class, an abstract class shall provide implementations of all contract members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. +Like a non-abstract class, an abstract class shall provide implementations of all abstract members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. > *Example*: > From 5bd2d4b12bdc547d26ded440dafdaac208d878e4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 23 Jul 2025 14:32:02 -0400 Subject: [PATCH 18/47] Rewrite most specific implementation rule This reads more consistently to me. Also, disallow reabstraction of interface methods in class types. --- standard/interfaces.md | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 743be157c..838cf2ea3 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -438,32 +438,16 @@ A virtual method with implementation declared in an interface may be overridden > > This is useful in derived interfaces where the default implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. *end example* -Reabstraction is also permissible in an implementing class. - -> *Example*: -> -> -> ```csharp -> interface I1 -> { -> void M() { } -> } -> -> abstract class C: I1 -> { -> public abstract void M(); // implement I1.M with an abstract method in C -> } -> ``` -> -> *end example* - - Every interface and class shall have a most specific implementation for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. -One override `M1` is considered *more specific* than another override `M2` if `M1` is declared on type `T1`, `M2` is declared on type `T2`, and either +> *Note*: The most specific override rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* -1. `T2` is an interface type but `T1` is not an interface type or -1. `T1` contains `T2` among its direct or indirect interfaces. +For a type `T` that is a struct or a class that implements interface `I` with a member `M`, the most specific implementation of `M` is: + +- If `T` declares an implementation of `I.M`, that implementation is the most specific implementation. +- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific implementation. +- Otherwise, if `I2` and `I3` are interfaces implemented by `T` and `I3` derives from `I2` either directly or indirectly, `I3.M` is a more specific implementation than `I2.M`. +- Otherwise, neither `I2.M` nor `I3.M` are more specific and an error occurs. > *Example*: > @@ -510,6 +494,10 @@ It is an error if in a class declaration the most specific override of some inte > > *end example* +It is an error if a property `P` has a most specific `get` accessor in one type `T1`, and a most specific `set` accessor in another type `T2`. + + + ### 18.4.3 Interface properties This clause augments the description of properties in classes [§15.7](classes.md#157-properties) for properties declared in interfaces. From 3b99e444e79aa3e8ee67341e35846de34b98c661 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 25 Jul 2025 10:39:35 -0400 Subject: [PATCH 19/47] lint issues --- standard/interfaces.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 838cf2ea3..8ee99f20f 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -496,8 +496,6 @@ It is an error if in a class declaration the most specific override of some inte It is an error if a property `P` has a most specific `get` accessor in one type `T1`, and a most specific `set` accessor in another type `T2`. - - ### 18.4.3 Interface properties This clause augments the description of properties in classes [§15.7](classes.md#157-properties) for properties declared in interfaces. From 696002bdb30f83c6b30a0dc43c709ebb0c00eaa7 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 20 Aug 2025 14:15:53 -0400 Subject: [PATCH 20/47] Apply suggestions from code review Co-authored-by: Jon Skeet --- standard/interfaces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 8ee99f20f..76ae95f17 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -244,7 +244,7 @@ An *interface_declaration* creates a new declaration space ([§7.3](basic-concep - The name of a type parameter in the *variant_type_parameter_list* of an interface declaration shall differ from the names of all other type parameters in the same *variant_type_parameter_list* and shall differ from the names of all members of the interface. - The name of a method shall differ from the names of all other kinds of function members declared in the same interface. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same interface, and two methods declared in the same interface may not have signatures that differ solely by `in`, `out`, and `ref`. -- The name of a non-function member shall differ from the names of all other non-function member declared in the same interface. +- The name of a non-function member shall differ from the names of all other non-function members declared in the same interface. - The signature of an indexer shall differ from the signatures of all other indexers declared in the same interface. The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does cause a compiler to issue a warning. To suppress the warning, the declaration of the derived interface member shall include a `new` modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in [§7.7.2.3](basic-concepts.md#7723-hiding-through-inheritance). @@ -309,7 +309,7 @@ As a static *field_declaration* is considered to have a default implementation ( > ```csharp > public interface IX > { -> public const int constant = 100; +> public const int Constant = 100; > protected static int field; > > static IX() @@ -508,7 +508,7 @@ An explicit interface member implementation shall not contain an *accessor_modif A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no default implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). -A derived interface mey implement an abstract interface property declared in a base interface. +A derived interface may implement an abstract interface property declared in a base interface. > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* From 8abcd9f08f45fc151a3ae2ee85220540846ec639 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Aug 2025 16:49:42 -0400 Subject: [PATCH 21/47] Respond to previous meeting feedback Make all edits except grammar changes. --- standard/interfaces.md | 265 +++++++++++++++++++---------------------- 1 file changed, 122 insertions(+), 143 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 76ae95f17..2ed963fc2 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,9 +4,9 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide ***default implementation***s for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. -> *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the default implementation as a non-breaking change; however, if their requirements are different, they can override the default implementations. *end note* +> *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* ## 18.2 Interface declarations @@ -230,32 +230,26 @@ interface_member_declaration ; ``` -An interface declaration declares zero or more members. The members of an interface shall be constants, fields, methods, properties, events, indexers, operators, constructors, and types, some of which may be instance, others static, as described in the subclauses for each interface member kind. Interfaces shall not declare instance data members, including field like events. +This clause augments the description of members in classes (§15.3). Interface members are declared using *member_declaration*s with the following additional rules: -All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted except on static constructors (§15.12). +- A *finalizer_declaration* is not allowed. +- Instance constructors, *constructor_declaration*s, are not allowed. +- All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted except on static constructors (§15.12). +- An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. +- The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. +- The `abstract` modifier is implied for interface function members without bodies; that modifier may be given explicitly. +- A derived interface may override an abstract member declared in a base interface. A non-virtual function member may be declared using the `sealed` keyword. +- A `private` or `sealed` function member of an interface shall have a body. +- A `private` function member shall not have the modifier `sealed`. +- An explicitly implemented function member shall not have the modifier `sealed`. -An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. Similarly, although `abstract` is implied for interface function members without bodies, that modifier may be given explicitly. A derived interface may override an abstract member declared in a base interface. A non-virtual function member may be declared using the `sealed` keyword. - -A `private` or `sealed` function member of an interface shall have a body. A `private` function member shall not have the modifier `sealed`. - -An explicitly implemented function member shall not have the modifier `sealed`. - -An *interface_declaration* creates a new declaration space ([§7.3](basic-concepts.md#73-declarations)), and the type parameters and *interface_member_declaration*s immediately contained by the *interface_declaration* introduce new members into this declaration space. The following rules apply to *interface_member_declaration*s: - -- The name of a type parameter in the *variant_type_parameter_list* of an interface declaration shall differ from the names of all other type parameters in the same *variant_type_parameter_list* and shall differ from the names of all members of the interface. -- The name of a method shall differ from the names of all other kinds of function members declared in the same interface. In addition, the signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) of a method shall differ from the signatures of all other methods declared in the same interface, and two methods declared in the same interface may not have signatures that differ solely by `in`, `out`, and `ref`. -- The name of a non-function member shall differ from the names of all other non-function members declared in the same interface. -- The signature of an indexer shall differ from the signatures of all other indexers declared in the same interface. - -The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does cause a compiler to issue a warning. To suppress the warning, the declaration of the derived interface member shall include a `new` modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in [§7.7.2.3](basic-concepts.md#7723-hiding-through-inheritance). - -If a `new` modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the `new` modifier. +The inherited members of an interface are specifically not part of the declaration space of the interface. The rules for inheritance in classes (§15.3.4) do not apply to interfaces. The `new` modifier can hide an interface member declared in a base interface. The rules for the `new` modifier in classes (§15.3.5) apply to interface member declarations. > *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -Consider an interface with a default implementation for a member `M`. As `M` is not part of that interface’s contract, outside that interface or any interface derived from it, that name is not visible. How then can it be accessed? The following code shows how: +Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is identity convertible to `IA`. ```csharp @@ -297,11 +291,12 @@ Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. ### §interface-fields Interface fields -This clause augments the description of fields in classes [§15.5](classes.md#155-fields) for fields declared in interfaces. *field_declaration* is described in [§15.5.1](classes.md#1551-general). +This clause augments the description of fields in classes [§15.5](classes.md#155-fields) for fields declared in interfaces. -It is a compile-time error for *field_declaration* to declare an instance field. +Interface fields are declared using *field_declaration*s ([§15.5.1](classes.md#1551-general)) with the following additional rules: -As a static *field_declaration* is considered to have a default implementation ([§18.1](interfaces.md#181-general)), it is *not* abstract. +- It is a compile-time error for *field_declaration* to declare an instance field. +- As a static *field_declaration* has an implementation ([§18.1](interfaces.md#181-general)), it is *not* abstract. > *Example*: The following program contains static members of various kinds: > @@ -347,33 +342,23 @@ See §interface-static-constructors for information regarding the allocation and This clause augments the description of methods in classes [§15.6](classes.md#156-methods) for methods declared in interfaces. -```ANTLR -interface_method_header - : identifier '(' parameter_list? ')' ';' - | identifier type_parameter_list '(' parameter_list? ')' - type_parameter_constraints_clause* ';' - ; -``` +Interface properties are declared using *method_declaration*s (§15.6)). The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules: -The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). +- *method_modifier* shall not include `override`. +- An interface method declaration that has a block body or expression body as a *method_body* has an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. +- A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. +- The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: + - A static declaration that is not extern shall have a block body or expression body as a *method_body*. + - A virtual declaration that is not extern shall have a block body or expression body as a *method_body*. + - A private declaration that is not extern shall have a block body or expression body as a *method_body*. + - A sealed declaration that is not extern shall have a block body or expression body as a *method_body*. + - An async declaration shall have a block body or expression body as a *method_body*. +- All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. +- Any output or reference parameter types shall also be output-safe. -*method_modifier* shall not include `override`. + > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* -An interface method declaration that has a block body or expression body as a *method_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. - -The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: - -- A static declaration that is not extern shall have a block body or expression body as a *method_body*. -- A virtual declaration that is not extern shall have a block body or expression body as a *method_body*. -- A private declaration that is not extern shall have a block body or expression body as a *method_body*. -- A sealed declaration that is not extern shall have a block body or expression body as a *method_body*. -- An async declaration shall have a block body or expression body as a *method_body*. - -All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference parameter types shall also be output-safe. - -> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* - -Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe. +- Each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe. These rules ensure that any covariant or contravariant usage of the interface remains typesafe. @@ -398,9 +383,9 @@ These rules ensure that any covariant or contravariant usage of the interface re > void M() where U : T; > } > class B {} -> class D: B {} -> class E: B {} -> class C: I +> class D : B {} +> class E : B {} +> class C : I > { > public void M() {...} > } @@ -417,7 +402,7 @@ These rules ensure that any covariant or contravariant usage of the interface re -> *Note*: See §interface-fields for an example that not only shows a static method with default implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point. *end note* +> *Note*: See §interface-fields for an example that not only shows a static method with an implementation, but as that method is called `Main` and has the right return type and signature, it’s also an entry point. *end note* A virtual method with implementation declared in an interface may be overridden to be abstract in a derived interface. This is known as ***reabstraction***. @@ -436,99 +421,34 @@ A virtual method with implementation declared in an interface may be overridden > } > ``` > -> This is useful in derived interfaces where the default implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. *end example* - -Every interface and class shall have a most specific implementation for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. - -> *Note*: The most specific override rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* - -For a type `T` that is a struct or a class that implements interface `I` with a member `M`, the most specific implementation of `M` is: - -- If `T` declares an implementation of `I.M`, that implementation is the most specific implementation. -- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific implementation. -- Otherwise, if `I2` and `I3` are interfaces implemented by `T` and `I3` derives from `I2` either directly or indirectly, `I3.M` is a more specific implementation than `I2.M`. -- Otherwise, neither `I2.M` nor `I3.M` are more specific and an error occurs. - -> *Example*: -> -> -> ```csharp -> interface IA -> { -> void M() { Console.WriteLine("IA.M"); } -> } -> -> interface IB : IA -> { -> void IA.M() { Console.WriteLine("IB.M"); } -> } -> -> interface IC: IA -> { -> void IA.M() { Console.WriteLine("IC.M"); } -> } -> -> abstract class C: IB, IC { } // error: no most specific override for 'IA.M' -> -> abstract class D: IA, IB, IC // OK -> { -> public abstract void M(); -> } -> ``` -> -> The most specific override rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* - -It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in a class or an interface. - -> *Example*: -> -> -> ```csharp -> interface IF -> { -> void M(); -> } -> -> abstract class F : IF { } // error: 'F' does not implement 'IF.M' -> ``` -> -> *end example* - -It is an error if a property `P` has a most specific `get` accessor in one type `T1`, and a most specific `set` accessor in another type `T2`. +> This is useful in derived interfaces where the implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. *end example* ### 18.4.3 Interface properties This clause augments the description of properties in classes [§15.7](classes.md#157-properties) for properties declared in interfaces. -Interface properties are declared using *property_declaration*s ([§15.7.1](classes.md#1571-general)). - -*property_modifier* shall not include `override`. - -An explicit interface member implementation shall not contain an *accessor_modifier* (§15.7.3). - -A *property_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no default implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +Interface properties are declared using *property_declaration*s ([§15.7.1](classes.md#1571-general)) with the following additional rules: -A derived interface may implement an abstract interface property declared in a base interface. +- *property_modifier* shall not include `override`. +- An explicit interface member implementation shall not contain an *accessor_modifier* (§15.7.3). +- A derived interface may implement an abstract interface property declared in a base interface. -> *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* + > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* -The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. +- The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. +- A *property_declaration* that has a *block* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). ### 18.4.4 Interface events This clause augments the description of events in classes [§15.8](classes.md#158-events) for events declared in interfaces. -Interface events are declared using *event_declaration*s ([§15.8.1](classes.md#1581-general)). - -*event_modifier* shall not include `override`. - -A derived interface may implement an abstract interface event declared in a base interface (§15.8.5). - -An *event_declaration* that has an *event_accessor_declarations* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An *event_declaration* that has no default implementation is always considered abstract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). - -It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. +Interface events are declared using *event_declaration*s ([§15.8.1](classes.md#1581-general)), with the following additional rules: -The type of an interface event shall be input-safe. +- *event_modifier* shall not include `override`. +- A derived interface may implement an abstract interface event declared in a base interface (§15.8.5). +- It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. +- The type of an interface event shall be input-safe. +- An *event_declaration* that has an *event_accessor_declarations* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An *event_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). ### 18.4.5 Interface indexers @@ -543,31 +463,30 @@ interface_indexer_declaration ; ``` -Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)). +Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: -*indexer_modifier* shall not include `override`. +- *indexer_modifier* shall not include `override`. +- An *indexer_declaration* that has a *block* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. +- All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). +- Any output or reference parameter types shall also be output-safe. -An *indexer_declaration* that has a *block* as an *accessor_body* is a default implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. + > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* -All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference parameter types shall also be output-safe. - -> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* - -The type of an interface indexer shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. +- The type of an interface indexer shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. ### §interface-operators Interface operators This clause augments the description of *operator_declaration* members in classes [§15.10](classes.md#1510-operators) for operators declared in interfaces. -An *operator_declaration* in an interface is a default implementation ([§18.1](interfaces.md#181-general)). +An *operator_declaration* in an interface is the implementation ([§18.1](interfaces.md#181-general)). -It is a compile-time error for an interface to contain a conversion, equality, or inequality operator. +It is a compile-time error for an interface to declare a conversion, equality, or inequality operator. ### §interface-static-constructors Interface static constructors This clause augments the description of static constructors in classes [§15.12](classes.md#1512-static-constructors) for static constructors declared in interfaces. -A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is a default implementation ([§18.1](interfaces.md#181-general)). +A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is the implementation ([§18.1](interfaces.md#181-general)). The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: @@ -596,6 +515,66 @@ It is an error to declare a class type, struct type, or enum type within the sco > > *end example* +### §most-specific-implementation Most specific implementation + +Every interface and class shall have a most specific implementation for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific implementation*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. + +> *Note*: The most specific implementation rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* + +For a type `T` that is a struct or a class that implements interfaces `I2` and `I3`, where `I2` and `I3` both derive directly or indirectly from interface `I` that declares a member `M`, the most specific implementation of `M` is: + +- If `T` declares an implementation of `I.M`, that implementation is the most specific implementation. +- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific implementation. +- Otherwise, if `I2` and `I3` are interfaces implemented by `T` and `I3` derives from `I2` either directly or indirectly, `I3.M` is a more specific implementation than `I2.M`. +- Otherwise, neither `I2.M` nor `I3.M` are more specific and an error occurs. + +> *Example*: +> +> +> ```csharp +> interface IA +> { +> void M() { Console.WriteLine("IA.M"); } +> } +> +> interface IB : IA +> { +> void IA.M() { Console.WriteLine("IB.M"); } +> } +> +> interface IC: IA +> { +> void IA.M() { Console.WriteLine("IC.M"); } +> } +> +> abstract class C: IB, IC { } // error: no most specific override for 'IA.M' +> +> abstract class D: IA, IB, IC // OK +> { +> public abstract void M(); +> } +> ``` +> +> The most specific override rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* + +It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in a class or an interface. + +> *Example*: +> +> +> ```csharp +> interface IF +> { +> void M(); +> } +> +> abstract class F : IF { } // error: 'F' does not implement 'IF.M' +> ``` +> +> *end example* + +It is an error if a property `P` has a most specific `get` accessor in one type `T1`, and a most specific `set` accessor in another type `T2`. + ### 18.4.6 Interface member access Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. @@ -880,7 +859,7 @@ For purposes of implementing interfaces, a class, struct, or interface may decla > > *end example* -It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit instance interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. An explicit interface static member implementation can only be accessed through the interface name. +It is not possible to access an explicit interface member implementation through its qualified interface member name in a method invocation, property access, event access, or indexer access. An explicit interface instance member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name. An explicit interface static member implementation can only be accessed through the interface name. It is a compile-time error for an explicit interface member implementation to include any modifiers ([§15.6](classes.md#156-methods)) other than `extern` or `async`. From 17865d05b5b7ec1b8f3217fbdf3f8e732671d116 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 22 Aug 2025 14:00:56 -0400 Subject: [PATCH 22/47] fix grammar and test issues Remove interface indexer grammar (it's the same as class grammar). Fix test issues (typo `constant` => `Constant` --- standard/interfaces.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 2ed963fc2..4b78ecb9e 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -310,7 +310,7 @@ Interface fields are declared using *field_declaration*s ([§15.5.1](classes.md# > static IX() > { > Console.WriteLine("static members initialized"); -> Console.WriteLine($"constant = {IX.constant}, field = {IX.field}"); +> Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); > field = 50; > Console.WriteLine("static constructor has run"); > } @@ -320,7 +320,7 @@ Interface fields are declared using *field_declaration*s ([§15.5.1](classes.md# > { > public static void Main() > { -> Console.WriteLine($"constant = {IX.constant}, field = {IX.field}"); +> Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); > } > } > ``` @@ -454,15 +454,6 @@ Interface events are declared using *event_declaration*s ([§15.8.1](classes.md# This clause augments the description of indexers in classes [§15.9](classes.md#159-indexers) for indexers declared in interfaces. -```ANTLR -interface_indexer_declaration - : attributes? 'new'? type 'this' '[' parameter_list ']' - '{' interface_accessors '}' - | attributes? 'new'? ref_kind type 'this' '[' parameter_list ']' - '{' ref_interface_accessor '}' - ; -``` - Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. From 5ee62319b6c9ff788d898abe6f2330bedb4665d0 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 22 Aug 2025 14:33:32 -0400 Subject: [PATCH 23/47] final pass at comments --- standard/interfaces.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 4b78ecb9e..d16aade55 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,9 +4,9 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. Members with an implementation may be declared virtual. -> *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change! The addition of default interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* +> *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* ## 18.2 Interface declarations @@ -235,10 +235,10 @@ This clause augments the description of members in classes (§15.3). Interface m - A *finalizer_declaration* is not allowed. - Instance constructors, *constructor_declaration*s, are not allowed. - All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted except on static constructors (§15.12). -- An interface function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. +- An interface instance function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. - The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. - The `abstract` modifier is implied for interface function members without bodies; that modifier may be given explicitly. -- A derived interface may override an abstract member declared in a base interface. A non-virtual function member may be declared using the `sealed` keyword. +- A derived interface may override an abstract member declared in a base interface. A non-virtual instance function member may be declared using the `sealed` keyword. - A `private` or `sealed` function member of an interface shall have a body. - A `private` function member shall not have the modifier `sealed`. - An explicitly implemented function member shall not have the modifier `sealed`. @@ -447,8 +447,9 @@ Interface events are declared using *event_declaration*s ([§15.8.1](classes.md# - *event_modifier* shall not include `override`. - A derived interface may implement an abstract interface event declared in a base interface (§15.8.5). - It is a compile-time error for *variable_declarators* in an instance *event_declaration* to contain any *variable_initializer*s. +- An instance event with the `virtual` or `sealed` modifiers must declare accessors. It is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). +- An instance event with the `abstract` modifier must not declare accessors. - The type of an interface event shall be input-safe. -- An *event_declaration* that has an *event_accessor_declarations* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An *event_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented field-like event ([§15.8.2](classes.md#1582-field-like-events)). ### 18.4.5 Interface indexers @@ -506,16 +507,16 @@ It is an error to declare a class type, struct type, or enum type within the sco > > *end example* -### §most-specific-implementation Most specific implementation +### §most-specific-implementation Most specific override -Every interface and class shall have a most specific implementation for every virtual member among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific implementation*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. +Every interface and class shall have a most specific override for every virtual member declared in all interfaces implemented by that type among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. -> *Note*: The most specific implementation rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* +> *Note*: The most specific override rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* -For a type `T` that is a struct or a class that implements interfaces `I2` and `I3`, where `I2` and `I3` both derive directly or indirectly from interface `I` that declares a member `M`, the most specific implementation of `M` is: +For a type `T` that is a struct or a class that implements interfaces `I2` and `I3`, where `I2` and `I3` both derive directly or indirectly from interface `I` that declares a member `M`, the most specific override of `M` is: -- If `T` declares an implementation of `I.M`, that implementation is the most specific implementation. -- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific implementation. +- If `T` declares an implementation of `I.M`, that implementation is the most specific override. +- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific override. - Otherwise, if `I2` and `I3` are interfaces implemented by `T` and `I3` derives from `I2` either directly or indirectly, `I3.M` is a more specific implementation than `I2.M`. - Otherwise, neither `I2.M` nor `I3.M` are more specific and an error occurs. From 23212e5f8582950171a058af7b81114cef678098 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 2 Sep 2025 16:00:53 -0400 Subject: [PATCH 24/47] Apply suggestions from code review Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/classes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 27fc755b9..418d57889 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -4,7 +4,7 @@ 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***. -Structs (§16) and interfaces (§18) provide data structures that contain members similar to classes. Rules restrict the declarations for struct and interface types, and the member declarations allowed in struct and interface types. This clause defines the declarations for classes and class members. The clauses for structs and interfaces define the restrictions for those types in terms of the corresponding declarations in class types. +Structs (§16) and interfaces (§18) have members similar to classes but with certain restrictions. This clause defines the declarations for classes and class members. The clauses for structs and interfaces define the restrictions for those types in terms of the corresponding declarations in class types. ## 15.2 Class declarations @@ -1534,7 +1534,7 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants -A ***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. A *constant_declaration* may appear in a class, struct, or interface declaration. +A ***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. A *constant_declaration* may appear in a class, struct, or interface declarations. ```ANTLR constant_declaration @@ -2917,7 +2917,7 @@ When an instance method declaration includes a `sealed` modifier, that method is When 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. +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. The *method_body* of an abstract method simply consists of a semicolon. Abstract method declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). @@ -3930,7 +3930,7 @@ An accessor that is used to implement an interface shall not have an *accessor_m A 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. +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. From 294c1aa990c234bf68cb67714679e272bfd9df95 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 2 Sep 2025 17:19:41 -0400 Subject: [PATCH 25/47] review comments, plus some noodling I need to clean up the notes from lines 578-587 to proper normative language. --- standard/classes.md | 2 +- standard/interfaces.md | 89 ++++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 418d57889..becb54051 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1534,7 +1534,7 @@ The following method names are reserved. While many have corresponding operators ## 15.4 Constants -A ***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. A *constant_declaration* may appear in a class, struct, or interface declarations. +A ***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. ```ANTLR constant_declaration diff --git a/standard/interfaces.md b/standard/interfaces.md index d16aade55..7ddb8db43 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -230,7 +230,7 @@ interface_member_declaration ; ``` -This clause augments the description of members in classes (§15.3). Interface members are declared using *member_declaration*s with the following additional rules: +This clause augments the description of members in classes (§15.3) with restrictions for interfaces. The Interface members are declared using *member_declaration*s with the following additional rules: - A *finalizer_declaration* is not allowed. - Instance constructors, *constructor_declaration*s, are not allowed. @@ -243,49 +243,51 @@ This clause augments the description of members in classes (§15.3). Interface m - A `private` function member shall not have the modifier `sealed`. - An explicitly implemented function member shall not have the modifier `sealed`. +Some declarations, such as *constant_declaration* (§15.4) have no restrictions in interfaces. + The inherited members of an interface are specifically not part of the declaration space of the interface. The rules for inheritance in classes (§15.3.4) do not apply to interfaces. The `new` modifier can hide an interface member declared in a base interface. The rules for the `new` modifier in classes (§15.3.5) apply to interface member declarations. > *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§18.4](interfaces.md#184-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is identity convertible to `IA`. - - -```csharp -interface IA -{ - public int P { get { return 10; } } - public void M() - { - Console.WriteLine("IA.M"); - } -} - -interface IB: IA -{ - public new int P { get { return 20; } } - void IA.M() - { - Console.WriteLine("IB.M"); - } -} - -class C: IB { } - -class Test -{ - public static void Main() - { - C c = new C(); - ((IA)c).M(); // cast needed - Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed - Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed - } -} -``` - -Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Start`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. +> *Example*: Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is identity convertible to `IA`. +> +> +> ```csharp +> interface IA +> { +> public int P { get { return 10; } } +> public void M() +> { +> Console.WriteLine("IA.M"); +> } +> } +> +> interface IB: IA +> { +> public new int P { get { return 20; } } +> void IA.M() +> { +> Console.WriteLine("IB.M"); +> } +> } +> +> class C: IB { } +> +> class Test +> { +> public static void Main() +> { +> C c = new C(); +> ((IA)c).M(); // cast needed +> Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed +> Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed +> } +> } +> ``` +> +>Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Main`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. *end example* > *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* @@ -573,6 +575,17 @@ Interface members are accessed through member access ([§12.8.7](expressions.md# For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. +Raw notes, that I will polish in the morning. This is based on noodling through the examples from Jon and Nigel: + +In a class `D`, with base class `B`, where `B` implements interface `I` and `I` defines a method `M()`. + +- The expression `base.M()` is valid only if `B` provides a definition for `M`. In general, `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. +- If the most specific implementation of `M` is in the interface `I`, the expression `(this as I).M()` is valid. +- If `B` contains a method `M` without the `virtual` or `override` modifier, that method hides `I.M()` rather than overriding it. +- If `B` contains a method `M` with a different parameter list, that's a new method. + +End raw notes. + > *Example*: In the following code > > From b7f40b1de10dc5d3a2afc6412160e1e36cfd64a5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 3 Sep 2025 10:43:31 -0400 Subject: [PATCH 26/47] Add normative rules for interface member resolution Finalize the normative language for resolution of members defined in an interface. --- standard/interfaces.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 7ddb8db43..e634a8ddb 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -242,6 +242,7 @@ This clause augments the description of members in classes (§15.3) with restric - A `private` or `sealed` function member of an interface shall have a body. - A `private` function member shall not have the modifier `sealed`. - An explicitly implemented function member shall not have the modifier `sealed`. +- The name of a non-abstract interface member is only visible in that interface and interfaces derived from it. That member can be accessed only through a reference whose compile-time type is an interface type that is implicitly convertible to the interface where the non-abstract member is declared. Some declarations, such as *constant_declaration* (§15.4) have no restrictions in interfaces. @@ -251,8 +252,8 @@ The inherited members of an interface are specifically not part of the declarati The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -> *Example*: Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is identity convertible to `IA`. -> +> *Example*: Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is an interface that is implicitly convertible to `IA`. +> > > ```csharp > interface IA @@ -288,7 +289,9 @@ The set of members of an interface declared in multiple parts ([§15.2.7](classe > ``` > >Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Main`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. *end example* + + > *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* ### §interface-fields Interface fields @@ -571,20 +574,16 @@ It is an error if a property `P` has a most specific `get` accessor in one type ### 18.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. If `M` is non-abstract, the name `M` is visible only in the interface `I` and interfaces derived from `I`. -For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. - -Raw notes, that I will polish in the morning. This is based on noodling through the examples from Jon and Nigel: +In a class `D`, with base class `B`, where `B` implements interface `I` and `I` defines a method `M()`: -In a class `D`, with base class `B`, where `B` implements interface `I` and `I` defines a method `M()`. - -- The expression `base.M()` is valid only if `B` provides a definition for `M`. In general, `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. -- If the most specific implementation of `M` is in the interface `I`, the expression `(this as I).M()` is valid. +- The expression `base.M()` is valid only if `B` provides a definition for `M`. The expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. +- If the most specific implementation of `M` is in the interface `I`, the expression `(this as I).M()` is valid and refers to the most specific implementation (§most-specific-implementation) of `M` in `I` or an interface derived from `I`. - If `B` contains a method `M` without the `virtual` or `override` modifier, that method hides `I.M()` rather than overriding it. -- If `B` contains a method `M` with a different parameter list, that's a new method. +- If `B` contains a method `M` with a different parameter list, that method `M` is a distinct overload of `M`. -End raw notes. +For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. > *Example*: In the following code > From a9c759c7d7ceb96214c25d54f3c749484a072e33 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 9 Sep 2025 16:50:38 -0400 Subject: [PATCH 27/47] first set of post-meeting updates. --- standard/basic-concepts.md | 12 ++++++------ standard/classes.md | 6 +++--- standard/interfaces.md | 15 ++++++--------- standard/namespaces.md | 2 +- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 0d587c165..fd4208aa6 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -78,7 +78,7 @@ There are several different types of declaration spaces, as described in the fol - Within all compilation units of a program, *namespace_member_declaration*s with no enclosing *namespace_declaration* are members of a single combined declaration space called the ***global declaration space***. - Within all compilation units of a program, *namespace_member_declaration*s within *namespace_declaration*s that have the same fully qualified namespace name are members of a single combined declaration space. - Each *compilation_unit* and *namespace_body* has an ***alias declaration space***. Each *extern_alias_directive* and *using_alias_directive* of the *compilation_unit* or *namespace_body* contributes a member to the alias declaration space ([§14.5.2](namespaces.md#1452-using-alias-directives)). -- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§16.2.4](structs.md#1624-partial-modifier)). Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class or struct cannot contain a member declaration with the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. +- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§16.2.4](structs.md#1624-partial-modifier)). Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class, interface, or struct cannot contain a member declaration with the same name as the class, interface, or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. - Each delegate declaration creates a new declaration space. Names are introduced into this declaration space through parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. - Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through *enum_member_declarations*. - Each method declaration, property declaration, property accessor declaration, indexer declaration, indexer accessor declaration, operator declaration, instance constructor declaration, anonymous function, and local function creates a new declaration space called a ***local variable declaration space***. Names are introduced into this declaration space through parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. The set accessor for a property or an indexer introduces the name `value` as a parameter. The body of the function member, anonymous function, or local function, if any, is considered to be nested within the local variable declaration space. When a local variable declaration space and a nested local variable declaration space contain elements with the same name, within the scope of the nested local name, the outer local name is hidden ([§7.7.1](basic-concepts.md#771-general)) by the nested local name. @@ -781,9 +781,9 @@ When a name in an inner scope hides a name in an outer scope, it hides all overl Name hiding through inheritance occurs when classes or structs redeclare names that were inherited from base classes. This type of name hiding takes one of the following forms: -- A constant, field, property, event, or type introduced in a class or struct hides all base class members with the same name. -- A method introduced in a class or struct hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). -- An indexer introduced in a class or struct hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . +- A constant, field, property, event, or type introduced in a class, interface, or struct hides all base class members with the same name. +- A method introduced in a class, interface, or struct hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). +- An indexer introduced in a class, interface, or struct hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . The rules governing operator declarations ([§15.10](classes.md#1510-operators)) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another. @@ -913,7 +913,7 @@ where: `R₀` is determined as follows: - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§15.6](classes.md#156-methods)) but outside the *attributes* of its *method-header*, and if that declaration includes a type parameter ([§15.2.3](classes.md#1523-type-parameters)) with name `I`, then `R₀` refers to that type parameter. -- Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class or struct declaration (if any): +- Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class, interface, or struct declaration (if any): - If `x` is zero and the declaration of `T` includes a type parameter with name `I`, then `R₀` refers to that type parameter. - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. > *Note*: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the *namespace_or_type_name*. *end note* @@ -945,7 +945,7 @@ For each repetition `n`, where `1 ≤ n ≤ k`, its resolution, `Rₙ`; which in - If `x` is zero and `Rₚ` refers to a namespace and `Rₚ` contains a nested namespace with name `I`, then `Rₙ` refers to that nested namespace. - Otherwise, if `Rₚ` refers to a namespace and `Rₚ` contains an accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. -- Otherwise, if `Rₚ` refers to a (possibly constructed) class or struct type and `Rₚ` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. +- Otherwise, if `Rₚ` refers to a (possibly constructed) class, interface, or struct type and `Rₚ` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. > *Note*: If the meaning of `T.I`, for some type `T`, is being determined as part of resolving the base class specification of `T` then the direct base class of `T` is considered to be `object` ([§15.2.4.2](classes.md#15242-base-classes)). *end note* - Otherwise, the *namespace_or_type_name* is invalid and a compile-time error occurs. diff --git a/standard/classes.md b/standard/classes.md index becb54051..1e6038474 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1098,7 +1098,7 @@ When a field, method, property, event, indexer, constructor, or finalizer declar #### 15.3.9.1 General -A 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***. +A type declared within a class, interface, or struct is called a ***nested type***. A type that is declared within a compilation unit or namespace is called a ***non-nested type***. > *Example*: In the following example: > @@ -1126,9 +1126,9 @@ The fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names) #### 15.3.9.3 Declared accessibility -Non-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: +Non-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, interface, or struct: -- A nested type that is declared in a class can have any of the permitted kinds of declared accessibility and, like other class members, defaults to `private` declared accessibility. +- A nested type that is declared in a class or interface can have any of the permitted kinds of declared accessibility and, like other class members, defaults to `private` declared accessibility. - A nested type that is declared in a struct can have any of three forms of declared accessibility (`public`, `internal`, or `private`) and, like other struct members, defaults to `private` declared accessibility. > *Example*: The example diff --git a/standard/interfaces.md b/standard/interfaces.md index e634a8ddb..3c7d9287a 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -265,7 +265,7 @@ The set of members of an interface declared in multiple parts ([§15.2.7](classe > } > } > -> interface IB: IA +> interface IB : IA > { > public new int P { get { return 20; } } > void IA.M() @@ -274,7 +274,7 @@ The set of members of an interface declared in multiple parts ([§15.2.7](classe > } > } > -> class C: IB { } +> class C : IB { } > > class Test > { @@ -570,16 +570,13 @@ It is an error if in a class declaration the most specific override of some inte > > *end example* -It is an error if a property `P` has a most specific `get` accessor in one type `T1`, and a most specific `set` accessor in another type `T2`. - ### 18.4.6 Interface member access Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. If `M` is non-abstract, the name `M` is visible only in the interface `I` and interfaces derived from `I`. -In a class `D`, with base class `B`, where `B` implements interface `I` and `I` defines a method `M()`: +In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`: -- The expression `base.M()` is valid only if `B` provides a definition for `M`. The expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. -- If the most specific implementation of `M` is in the interface `I`, the expression `(this as I).M()` is valid and refers to the most specific implementation (§most-specific-implementation) of `M` in `I` or an interface derived from `I`. +- The expression `base.M()` is valid only if `B` provides a definition for `M`. The expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. To access the most specific implementation if it was defined in an interface, use the cast expression `(this as I).M()` to refer to the most specific implementation (§most-specific-implementation) of `M` in `I` or an interface derived from `I`. - If `B` contains a method `M` without the `virtual` or `override` modifier, that method hides `I.M()` rather than overriding it. - If `B` contains a method `M` with a different parameter list, that method `M` is a distinct overload of `M`. @@ -1082,7 +1079,7 @@ When a generic method implicitly implements an interface method, the constraints A class or struct shall provide implementations of all abstract members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as ***interface mapping***. -Interface mapping for a class or struct `C` locates an implementation for each member of each interface specified in the base class list of `C`. The implementation of a particular interface member `I.M`, where `I` is the interface in which the member `M` is declared, is determined by examining each class or struct `S`, starting with `C` and repeating for each successive base class of `C`, until a match is located: +Interface mapping for a class or struct `C` locates an implementation for each member of each interface specified in the base class list of `C`. The implementation of a particular interface member `I.M`, where `I` is the interface in which the member `M` is declared, is determined by examining each class, interface, or struct `S`, starting with `C` and repeating for each successive base class and implemented interface of `C`, until a match is located: - If `S` contains a declaration of an explicit interface member implementation that matches `I` and `M`, then this member is the implementation of `I.M`. - Otherwise, if `S` contains a declaration of a non-static public member that matches `M`, then this member is the implementation of `I.M`. If more than one member matches, it is unspecified which member is the implementation of `I.M`. This situation can only occur if `S` is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical. @@ -1111,7 +1108,7 @@ Members of a constructed interface type are considered to have any type paramete > > *end example* -For purposes of interface mapping, a class or struct member `A` matches an interface member `B` when: +For purposes of interface mapping, a class, interface, or struct member `A` matches an interface member `B` when: - `A` and `B` are methods, and the name, type, and parameter lists of `A` and `B` are identical. - `A` and `B` are properties, the name and type of `A` and `B` are identical, and `A` has the same accessors as `B` (`A` is permitted to have additional accessors if it is not an explicit interface member implementation). diff --git a/standard/namespaces.md b/standard/namespaces.md index 754c9cb87..7763b5831 100644 --- a/standard/namespaces.md +++ b/standard/namespaces.md @@ -770,7 +770,7 @@ A *type_declaration* can occur as a top-level declaration in a compilation unit When a type declaration for a type `T` occurs as a top-level declaration in a compilation unit, the fully qualified name ([§7.8.2](basic-concepts.md#782-unqualified-names)) of the type declaration is the same as the unqualified name of the declaration ([§7.8.2](basic-concepts.md#782-unqualified-names)). When a type declaration for a type `T` occurs within a namespace, class, or struct declaration, the fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names)) of the type declarationis `S.N`, where `S` is the fully qualified name of the containing namespace, class, or struct declaration, and `N` is the unqualified name of the declaration. -A type declared within a class or struct is called a nested type ([§15.3.9](classes.md#1539-nested-types)). +A type declared within a class, interface, or struct is called a nested type ([§15.3.9](classes.md#1539-nested-types)). The permitted access modifiers and the default access for a type declaration depend on the context in which the declaration takes place ([§7.5.2](basic-concepts.md#752-declared-accessibility)): From b7b025e1c3084825a93cdecf7e2e15641873c83b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 10 Sep 2025 10:34:03 -0400 Subject: [PATCH 28/47] respond to review feedback. --- standard/interfaces.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 3c7d9287a..93216632b 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -301,7 +301,6 @@ This clause augments the description of fields in classes [§15.5](classes.md#15 Interface fields are declared using *field_declaration*s ([§15.5.1](classes.md#1551-general)) with the following additional rules: - It is a compile-time error for *field_declaration* to declare an instance field. -- As a static *field_declaration* has an implementation ([§18.1](interfaces.md#181-general)), it is *not* abstract. > *Example*: The following program contains static members of various kinds: > @@ -483,12 +482,13 @@ It is a compile-time error for an interface to declare a conversion, equality, o This clause augments the description of static constructors in classes [§15.12](classes.md#1512-static-constructors) for static constructors declared in interfaces. -A *static_constructor_declaration* that has a *block* as a *static_constructor_body* is the implementation ([§18.1](interfaces.md#181-general)). - The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: - Any of the static members of the interface are referenced. - Before the Main method is called for an interface containing the Main method ([§7.1](basic-concepts.md#71-application-startup)) in which execution begins. +- The implementation for a member defined in the interface is accessed as the most specific implementation (§most-specific-implementation) for that member. + +> *Note*: In the case where none of the preceding actions take place, the static constructor for an interface may not execute for a program where instances of types that implement the interface are created and used. *end note* To initialize a new closed interface type, first a new set of static fields for that particular closed type is created. Each of the static fields is initialized to its default value. Next, the static field initializers are executed for those static fields. Finally, the static constructor is executed. From 28e33919f38b49d86b49537a19e74a1c839b9e14 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 10 Sep 2025 17:27:41 -0400 Subject: [PATCH 29/47] Clarify more on access to interface members Clarify the rules on overriding interface members with implementations, and on accessing members with an implementation in an interface. --- standard/interfaces.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 93216632b..ddeace4f1 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. Members with an implementation may be declared virtual. +Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* @@ -235,14 +235,16 @@ This clause augments the description of members in classes (§15.3) with restric - A *finalizer_declaration* is not allowed. - Instance constructors, *constructor_declaration*s, are not allowed. - All interface members implicitly have public access; however, an explicit access modifier ([§7.5.2](basic-concepts.md#752-declared-accessibility)) is permitted except on static constructors (§15.12). -- An interface instance function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. -- The `virtual` modifier may be used on a function member that would otherwise be implicitly `virtual`. - The `abstract` modifier is implied for interface function members without bodies; that modifier may be given explicitly. -- A derived interface may override an abstract member declared in a base interface. A non-virtual instance function member may be declared using the `sealed` keyword. +- An interface instance function member whose declaration includes a body is an implicitly `virtual` member unless the `sealed` or `private` modifier is used. - A `private` or `sealed` function member of an interface shall have a body. - A `private` function member shall not have the modifier `sealed`. +- A derived interface may override an abstract member declared in a base interface. - An explicitly implemented function member shall not have the modifier `sealed`. -- The name of a non-abstract interface member is only visible in that interface and interfaces derived from it. That member can be accessed only through a reference whose compile-time type is an interface type that is implicitly convertible to the interface where the non-abstract member is declared. + +An abstract member `M1` may be overridden by either a derived interface or an implementing class or struct. That member `M1` can be accessed through a reference of the implementing type. + +A member `M2` with definition in an interface requires any overriding member to declare an explicit interface implementation (§18.6.2). That member `M2` only be accessed through an interface instance. Some declarations, such as *constant_declaration* (§15.4) have no restrictions in interfaces. @@ -440,7 +442,7 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* - The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -- A *property_declaration* that has a *block* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +- A *property_declaration* that has a *block* or *expression-bodied-member* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). ### 18.4.4 Interface events @@ -462,7 +464,7 @@ This clause augments the description of indexers in classes [§15.9](classes.md# Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. -- An *indexer_declaration* that has a *block* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. +- An *indexer_declaration* that has a *block* or *expression-bodied-member* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. - All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). - Any output or reference parameter types shall also be output-safe. @@ -572,7 +574,7 @@ It is an error if in a class declaration the most specific override of some inte ### 18.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. If `M` is non-abstract, the name `M` is visible only in the interface `I` and interfaces derived from `I`. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`: From 0b58bddf61d227b2c830c914726a12a96a439fa0 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Sep 2025 13:59:00 -0400 Subject: [PATCH 30/47] Apply suggestions from code review Co-authored-by: Joseph Musser --- standard/interfaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index ddeace4f1..48bc68e67 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -442,7 +442,7 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* - The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -- A *property_declaration* that has a *block* or *expression-bodied-member* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +- A *property_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). ### 18.4.4 Interface events @@ -464,7 +464,7 @@ This clause augments the description of indexers in classes [§15.9](classes.md# Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. -- An *indexer_declaration* that has a *block* or *expression-bodied-member* as an *accessor_body* is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. +- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. - All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). - Any output or reference parameter types shall also be output-safe. From f424d4f0955973b5e6483587faecf7a92a7b5fb9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Sep 2025 14:09:41 -0400 Subject: [PATCH 31/47] update anchor --- standard/interfaces.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 797b3554a..71770d8cb 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -344,7 +344,7 @@ Interface fields are declared using *field_declaration*s ([§15.5.1](classes.md# See §interface-static-constructors for information regarding the allocation and initialization of static fields. -### 18.4.2 Interface methods +### 19.4.2 Interface methods This clause augments the description of methods in classes [§15.6](classes.md#156-methods) for methods declared in interfaces. @@ -429,7 +429,7 @@ A virtual method with implementation declared in an interface may be overridden > > This is useful in derived interfaces where the implementation of a method is inappropriate and a more appropriate implementation should be provided by implementing classes. *end example* -### 18.4.3 Interface properties +### 19.4.3 Interface properties This clause augments the description of properties in classes [§15.7](classes.md#157-properties) for properties declared in interfaces. @@ -444,7 +444,7 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas - The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. - A *property_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). -### 18.4.4 Interface events +### 19.4.4 Interface events This clause augments the description of events in classes [§15.8](classes.md#158-events) for events declared in interfaces. @@ -572,7 +572,7 @@ It is an error if in a class declaration the most specific override of some inte > > *end example* -### 18.4.6 Interface member access +### 19.4.6 Interface member access Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. From 0c039de8aca660529bde91ea3fb14491a42c9f59 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Sep 2025 14:13:06 -0400 Subject: [PATCH 32/47] update xrefs --- standard/classes.md | 2 +- standard/interfaces.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 15e861daf..5e10fca45 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -4303,7 +4303,7 @@ An abstract event declaration specifies that the accessors of the event are virt 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 ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.4](interfaces.md#1844-interface-events)). +Abstract event declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§19.4.4](interfaces.md#1944-interface-events)). 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. diff --git a/standard/interfaces.md b/standard/interfaces.md index 71770d8cb..be2ec18dc 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -244,7 +244,7 @@ This clause augments the description of members in classes (§15.3) with restric An abstract member `M1` may be overridden by either a derived interface or an implementing class or struct. That member `M1` can be accessed through a reference of the implementing type. -A member `M2` with definition in an interface requires any overriding member to declare an explicit interface implementation (§18.6.2). That member `M2` only be accessed through an interface instance. +A member `M2` with definition in an interface requires any overriding member to declare an explicit interface implementation (§19.6.2). That member `M2` only be accessed through an interface instance. Some declarations, such as *constant_declaration* (§15.4) have no restrictions in interfaces. @@ -359,7 +359,7 @@ Interface properties are declared using *method_declaration*s (§15.6)). The *at - A private declaration that is not extern shall have a block body or expression body as a *method_body*. - A sealed declaration that is not extern shall have a block body or expression body as a *method_body*. - An async declaration shall have a block body or expression body as a *method_body*. -- All parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. +- All parameter types of an interface method shall be input-safe ([§19.2.3.2](interfaces.md#19232-variance-safety)), and the return type shall be either `void` or output-safe. - Any output or reference parameter types shall also be output-safe. > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* @@ -464,8 +464,8 @@ This clause augments the description of indexers in classes [§15.9](classes.md# Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. -- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. -- All the parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). +- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. +- All the parameter types of an interface indexer shall be input-safe ([§19.2.3.2](interfaces.md#19232-variance-safety)). - Any output or reference parameter types shall also be output-safe. > *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note* @@ -500,7 +500,7 @@ To initialize a new closed interface type, first a new set of static fields for This clause augments the description of nested types in classes [§15.3.9](classes.md#1539-nested-types) for nested types declared in interfaces. -It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§18.2.3.1](interfaces.md#18231-general)). +It is an error to declare a class type, struct type, or enum type within the scope of a type parameter that was declared with a *variance_annotation* ([§19.2.3.1](interfaces.md#19231-general)). > *Example*: The declaration of `C` below is an error. > From 2eb6fe03fc014b0d61e46d910f8f6594a09ca389 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Sep 2025 14:19:14 -0400 Subject: [PATCH 33/47] more anchor cleanup --- standard/classes.md | 6 +++--- standard/interfaces.md | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/standard/classes.md b/standard/classes.md index 5e10fca45..dec7b8526 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -2051,7 +2051,7 @@ A variable initializer for an instance field cannot reference the instance being ### 15.6.1 General -[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§16.4](structs.md#164-class-and-struct-differences)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). +[§15.6](classes.md#156-methods) and its subclauses cover method declarations in classes. That text is augmented by information about declaring methods in structs ([§16.4](structs.md#164-class-and-struct-differences)) and interfaces ([§19.4.2](interfaces.md#1942-interface-methods)). A ***method*** is a member that implements a computation or action that can be performed by an object or class. Methods are declared using *method_declaration*s: @@ -2920,7 +2920,7 @@ When an instance method declaration includes an `abstract` modifier, that method 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. The *method_body* of an abstract method simply consists of a semicolon. -Abstract method declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.2](interfaces.md#1842-interface-methods)). +Abstract method declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§19.4.2](interfaces.md#1942-interface-methods)). > *Example*: In the following code > @@ -3936,7 +3936,7 @@ An abstract property declaration specifies that the accessors of the property ar 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 ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§18.4.3](interfaces.md#1843-interface-properties)). 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. +Abstract property declarations are only permitted in abstract classes ([§15.2.2.2](classes.md#15222-abstract-classes)) and interfaces ([§19.4.3](interfaces.md#1943-interface-properties)). 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 shall 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. diff --git a/standard/interfaces.md b/standard/interfaces.md index be2ec18dc..8bb8a3b02 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§18.4](interfaces.md#184-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* @@ -351,7 +351,7 @@ This clause augments the description of methods in classes [§15.6](classes.md#1 Interface properties are declared using *method_declaration*s (§15.6)). The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules: - *method_modifier* shall not include `override`. -- An interface method declaration that has a block body or expression body as a *method_body* has an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. +- An interface method declaration that has a block body or expression body as a *method_body* has an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. - A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. - The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: - A static declaration that is not extern shall have a block body or expression body as a *method_body*. @@ -442,7 +442,7 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* - The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -- A *property_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§18.1](interfaces.md#181-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +- A *property_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). ### 19.4.4 Interface events @@ -476,7 +476,7 @@ Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.m This clause augments the description of *operator_declaration* members in classes [§15.10](classes.md#1510-operators) for operators declared in interfaces. -An *operator_declaration* in an interface is the implementation ([§18.1](interfaces.md#181-general)). +An *operator_declaration* in an interface is the implementation ([§19.1](interfaces.md#191-general)). It is a compile-time error for an interface to declare a conversion, equality, or inequality operator. @@ -574,7 +574,7 @@ It is an error if in a class declaration the most specific override of some inte ### 19.4.6 Interface member access -Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. +Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`: @@ -582,7 +582,7 @@ In a class `D`, with direct or indirect base class `B`, where `B` directly or in - If `B` contains a method `M` without the `virtual` or `override` modifier, that method hides `I.M()` rather than overriding it. - If `B` contains a method `M` with a different parameter list, that method `M` is a distinct overload of `M`. -For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.3](expressions.md#128123-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. +For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. > *Example*: In the following code > From 53f2bab5bd15a4ef38ad8d0e1ddf06997c85061a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 15 Sep 2025 11:57:29 -0400 Subject: [PATCH 34/47] Update grammar tests for DIM --- .../part-H/Reference/sample.gruntree.red.txt | 53 +- .../part-H/Reference/sample.stderr.txt | 0 .../part-H/Reference/sample.tree.red.txt | 2 +- .../part-H/Reference/sample.tree.svg | 2309 +++++++++-------- 4 files changed, 1217 insertions(+), 1147 deletions(-) create mode 100644 tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.stderr.txt diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.gruntree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.gruntree.red.txt index 53e3487ad..fe5647038 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.gruntree.red.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.gruntree.red.txt @@ -730,16 +730,20 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_member_declaration ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_method_declaration +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ method_declaration +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ method_modifiers ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ return_type ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ void ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_method_header +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ method_header ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ A +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ member_name +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ A +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ( ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ @@ -763,6 +767,9 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ) +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ method_body ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ; ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ @@ -770,7 +777,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_member_declaration ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_property_declaration +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ property_declaration ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ @@ -779,18 +786,36 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ Value +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ member_name +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ Value +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ { ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ interface_accessors -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ get -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ; -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ set -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ; +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ property_body +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ { +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ accessor_declarations +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ get_accessor_declaration +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ get +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ accessor_body +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ; +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ set_accessor_declaration +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ set +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ accessor_body +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ; +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ } ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ } ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ } diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.stderr.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.stderr.txt new file mode 100644 index 000000000..e69de29bb diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.red.txt index c3566db3d..18c40e873 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.red.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.red.txt @@ -1 +1 @@ -(prog (compilation_unit (namespace_declaration namespace (qualified_identifier (identifier My)) (namespace_body { (namespace_member_declaration (struct_declaration (struct_modifier public) struct (identifier S) (struct_interfaces : (interface_type_list (identifier I))) (struct_body { (struct_member_declaration (constructor_declaration (constructor_modifier public) (constructor_declarator (identifier S) ( )) (constructor_body (block { })))) (struct_member_declaration (field_declaration (field_modifier private) (type (integral_type int)) (variable_declarators (identifier f1)) ;)) (struct_member_declaration (field_declaration (attributes (attribute_section [ (attribute_list (attribute (attribute_name (identifier Obsolete)) (attribute_arguments ( (positional_argument_list (positional_argument (literal "Use Script instead")) , (positional_argument (argument_name (identifier error) :) (attribute_argument_expression (boolean_literal false)))) )))) ])) (field_modifier private) (field_modifier volatile) (type (integral_type int)) (variable_declarators (identifier f2)) ;)) (struct_member_declaration (method_declaration (method_modifiers (method_modifier (ref_method_modifier public)) (method_modifier (ref_method_modifier abstract))) (return_type (integral_type int)) (method_header (member_name (identifier m)) (type_parameter_list < (decorated_type_parameter (identifier T)) >) ( (parameter_list (fixed_parameter (type (identifier T)) (identifier t))) ) (type_parameter_constraints_clause where (type_parameter (identifier T)) : (type_parameter_constraints (primary_constraint struct)))) (method_body (block { (statement_list (return_statement return (expression (literal 1)) ;)) })))) (struct_member_declaration (property_declaration (property_modifier public) (type (class_type string)) (member_name (identifier P)) (property_body { (accessor_declarations (get_accessor_declaration get (accessor_body (block { (statement_list (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier (contextual_keyword value)) = (local_variable_initializer (literal 0)))))) ;)) (statement (return_statement return (expression (literal "A")) ;))) }))) (set_accessor_declaration set (accessor_body ;))) }))) (struct_member_declaration (property_declaration (property_modifier public) (property_modifier abstract) (type (class_type string)) (member_name (identifier P)) (property_body { (accessor_declarations (get_accessor_declaration get (accessor_body ;))) }))) (struct_member_declaration (indexer_declaration (indexer_modifier public) (indexer_modifier abstract) (indexer_declarator (type (integral_type int)) this [ (parameter_list (fixed_parameter (type (integral_type int)) (identifier index))) ]) (indexer_body { (accessor_declarations (get_accessor_declaration get (accessor_body ;)) (set_accessor_declaration (accessor_modifier internal protected) set (accessor_body ;))) }))) (struct_member_declaration (event_declaration (event_modifier public) event (type (identifier Event)) (variable_declarators (identifier E)) ;)) (struct_member_declaration (operator_declaration (operator_modifier public) (operator_modifier static) (operator_declarator (binary_operator_declarator (type (identifier A)) operator (overloadable_binary_operator +) ( (fixed_parameter (type (identifier A)) (identifier first)) , (fixed_parameter (type (identifier A)) (identifier second)) ))) (operator_body (block { (statement_list (return_statement return (expression (invocation_expression (primary_expression (member_access (primary_expression (identifier first)) . (identifier Add))) ( (argument_list (identifier second)) ))) ;)) })))) (struct_member_declaration (fixed_size_buffer_declaration fixed (buffer_element_type (integral_type int)) (fixed_size_buffer_declarators (fixed_size_buffer_declarator (identifier field) [ (constant_expression (literal 10)) ])) ;)) (struct_member_declaration (class_declaration class (identifier C) (class_body { }))) }))) (namespace_member_declaration (interface_declaration (interface_modifier public) interface (identifier I) (interface_body { (interface_member_declaration (interface_method_declaration (return_type void) (interface_method_header (identifier A) ( (parameter_list (fixed_parameter (type (integral_type int)) (identifier (contextual_keyword value)))) ) ;))) (interface_member_declaration (interface_property_declaration (type (class_type string)) (identifier Value) { (interface_accessors get ; set ;) })) }))) })))) +(prog (compilation_unit (namespace_declaration namespace (qualified_identifier (identifier My)) (namespace_body { (namespace_member_declaration (struct_declaration (struct_modifier public) struct (identifier S) (struct_interfaces : (interface_type_list (identifier I))) (struct_body { (struct_member_declaration (constructor_declaration (constructor_modifier public) (constructor_declarator (identifier S) ( )) (constructor_body (block { })))) (struct_member_declaration (field_declaration (field_modifier private) (type (integral_type int)) (variable_declarators (identifier f1)) ;)) (struct_member_declaration (field_declaration (attributes (attribute_section [ (attribute_list (attribute (attribute_name (identifier Obsolete)) (attribute_arguments ( (positional_argument_list (positional_argument (literal "Use Script instead")) , (positional_argument (argument_name (identifier error) :) (attribute_argument_expression (boolean_literal false)))) )))) ])) (field_modifier private) (field_modifier volatile) (type (integral_type int)) (variable_declarators (identifier f2)) ;)) (struct_member_declaration (method_declaration (method_modifiers (method_modifier (ref_method_modifier public)) (method_modifier (ref_method_modifier abstract))) (return_type (integral_type int)) (method_header (member_name (identifier m)) (type_parameter_list < (decorated_type_parameter (identifier T)) >) ( (parameter_list (fixed_parameter (type (identifier T)) (identifier t))) ) (type_parameter_constraints_clause where (type_parameter (identifier T)) : (type_parameter_constraints (primary_constraint struct)))) (method_body (block { (statement_list (return_statement return (expression (literal 1)) ;)) })))) (struct_member_declaration (property_declaration (property_modifier public) (type (class_type string)) (member_name (identifier P)) (property_body { (accessor_declarations (get_accessor_declaration get (accessor_body (block { (statement_list (statement (declaration_statement (local_variable_declaration (explicitly_typed_local_variable_declaration (type (integral_type int)) (explicitly_typed_local_variable_declarators (explicitly_typed_local_variable_declarator (identifier (contextual_keyword value)) = (local_variable_initializer (literal 0)))))) ;)) (statement (return_statement return (expression (literal "A")) ;))) }))) (set_accessor_declaration set (accessor_body ;))) }))) (struct_member_declaration (property_declaration (property_modifier public) (property_modifier abstract) (type (class_type string)) (member_name (identifier P)) (property_body { (accessor_declarations (get_accessor_declaration get (accessor_body ;))) }))) (struct_member_declaration (indexer_declaration (indexer_modifier public) (indexer_modifier abstract) (indexer_declarator (type (integral_type int)) this [ (parameter_list (fixed_parameter (type (integral_type int)) (identifier index))) ]) (indexer_body { (accessor_declarations (get_accessor_declaration get (accessor_body ;)) (set_accessor_declaration (accessor_modifier internal protected) set (accessor_body ;))) }))) (struct_member_declaration (event_declaration (event_modifier public) event (type (identifier Event)) (variable_declarators (identifier E)) ;)) (struct_member_declaration (operator_declaration (operator_modifier public) (operator_modifier static) (operator_declarator (binary_operator_declarator (type (identifier A)) operator (overloadable_binary_operator +) ( (fixed_parameter (type (identifier A)) (identifier first)) , (fixed_parameter (type (identifier A)) (identifier second)) ))) (operator_body (block { (statement_list (return_statement return (expression (invocation_expression (primary_expression (member_access (primary_expression (identifier first)) . (identifier Add))) ( (argument_list (identifier second)) ))) ;)) })))) (struct_member_declaration (fixed_size_buffer_declaration fixed (buffer_element_type (integral_type int)) (fixed_size_buffer_declarators (fixed_size_buffer_declarator (identifier field) [ (constant_expression (literal 10)) ])) ;)) (struct_member_declaration (class_declaration class (identifier C) (class_body { }))) }))) (namespace_member_declaration (interface_declaration (interface_modifier public) interface (identifier I) (interface_body { (interface_member_declaration (method_declaration method_modifiers (return_type void) (method_header (member_name (identifier A)) ( (parameter_list (fixed_parameter (type (integral_type int)) (identifier (contextual_keyword value)))) )) (method_body ;))) (interface_member_declaration (property_declaration (type (class_type string)) (member_name (identifier Value)) (property_body { (accessor_declarations (get_accessor_declaration get (accessor_body ;)) (set_accessor_declaration set (accessor_body ;))) }))) }))) })))) diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.svg b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.svg index ca86b258d..70703ce21 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.svg +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-H/Reference/sample.tree.svg @@ -1,12 +1,12 @@ - - - - + + + + - - - + + + @@ -326,1535 +326,1580 @@ - - - + + + - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -variable_declarators - - - -abstract - - - -literal - - - -local_variable_declaration - - - -event_modifier - - - -type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +accessor_body - - -this + + +m - - -property_declaration + + +set_accessor_declaration - - -field_modifier + + +statement - - -set_accessor_declaration + + +integral_type - - -: + + +identifier - - -; + + +accessor_body - - -) + + +. - - -indexer_declaration + + +identifier - - -literal + + +integral_type - - -set + + +variable_declarators - - -Obsolete + + +fixed_parameter - - -string + + +interface_member_declaration - - -identifier + + +Value - - -event + + +fixed_size_buffer_declarators - - -type + + +method_modifier - - -public + + +statement_list - - + + struct_member_declaration - - -{ + + +constructor_body - - -where + + +} - - -identifier + + +accessor_declarations - - -S + + +property_modifier - - + + identifier - - -get - - - -struct + + +operator_declaration - - -type + + +block - - -1 + + +; - - + + { - - -{ + + +public - - + + type - - -} + + +f1 - - -{ + + +, - - -statement + + +member_name - - -integral_type + + +property_modifier - - -public + + +variable_declarators - - -[ + + +field_modifier - - -int + + +literal - - -operator_body + + +private - - -int + + +{ - - -operator_modifier + + +positional_argument - - -] + + +argument_name - - -method_modifiers + + +int - - -invocation_expression + + +method_modifiers - - -struct_member_declaration + + +accessor_body - - -literal + + +fixed_parameter - - -integral_type + + +type_parameter_list - - -ref_method_modifier + + +return_type - - -member_name + + +( - - + + } - - -) - - - -string + + +indexer_declaration - - -method_declaration + + +abstract - - -+ + + +identifier - - -; + + +struct - - -Event + + +return_type - - -public + + +event_declaration - - -( + + +explicitly_typed_local_variable_declarators - - + + ; - - -set + + +decorated_type_parameter - - -statement_list + + +statement - - -class_type + + +internal - - -block + + +property_declaration - - -return + + +] - - -accessor_body + + +indexer_body - - -identifier + + +property_body - - -type + + +A - - -buffer_element_type + + +struct_member_declaration - - -( + + +method_declaration - - + + +type + + + identifier - - -interface_property_declaration + + +field_modifier - - -; + + +field_declaration - - -type + + +set_accessor_declaration ) - - -identifier - - - -} + + +operator_body - - -identifier + + +value - - -identifier + + +struct_member_declaration - - -property_modifier + + +string - - -return_type + + +return_statement - - -struct_member_declaration + + +contextual_keyword - - -int + + +return - - -( + + +identifier - - -volatile + + +indexer_modifier - - -integral_type + + +set - - -accessor_modifier + + +Add - - -operator + + +namespace_declaration - - -class_type + + +[ - - -E + + +struct_member_declaration - - + + identifier - - -{ + + +( - - -} + + +indexer_declarator - - -operator_modifier + + +accessor_body - - -parameter_list + + +} - - -accessor_declarations + + +constructor_modifier - - -; + + +identifier - - -property_modifier + + +identifier - - -block + + +; - - + + identifier - - -, + + +< - - -fixed_size_buffer_declarator + + +struct_member_declaration - - -ref_method_modifier + + +literal - - -constructor_modifier + + +type_parameter - - -property_body + + +expression - - -m + + +] - - -contextual_keyword + + +interface_modifier - - -f2 + + +operator_modifier + + + +operator - - + + } - - -error + + +int - - + + +identifier + + + +"Use·Script·instead" + + + ; - - -struct_body + + +identifier - - -} + + +P - - -private + + +A - - -identifier + + +struct_modifier - - -integral_type + + +ref_method_modifier - - -10 + + +C - - -integral_type + + +abstract - - -struct_member_declaration + + +variable_declarators - - + + member_name - - -indexer_modifier - - - -} + + +declaration_statement - - -{ + + +event_modifier - - -class_type + + +field - - -get_accessor_declaration + + +integral_type - - -identifier + + +accessor_declarations - - -A + + ++ - - -compilation_unit + + +accessor_declarations - - -namespace_member_declaration + + +; - - -{ + + +attribute_section - - -indexer_body + + +get - - -: + + +, - - -identifier + + +type - - -interface_member_declaration + + +{ - - -) + + +type - - -] + + +attribute_arguments - - -int + + +) - - -return + + +10 + + + +struct_member_declaration : - - -operator_declarator + + +identifier - - -integral_type + + +public - - -struct_interfaces + + +expression + + + +type + + + +} + + + +overloadable_binary_operator + + + +method_header identifier - - -parameter_list + + +S - - -property_body + + +identifier - - -struct + + +statement_list - - -qualified_identifier + + +: + + + +identifier + + + +attributes } - - -; + + +private - - -] + + +method_body - - -attribute_name + + +type - - + + ( - - + + identifier - - -identifier + + +; - - -integral_type + + +property_declaration - - -fixed_parameter + + +get_accessor_declaration - - -primary_expression + + +buffer_element_type - - + + identifier - - -namespace_declaration + + +set - - -indexer_modifier + + +public - - -static + + +constant_expression + + + +I method_modifier - - -integral_type + + +A - - -indexer_declarator + + +( - - -positional_argument + + +] - - -argument_name + + +get_accessor_declaration + + + +value - - -interface_accessors + + +field_declaration - - -struct_modifier + + +} - - -interface_method_declaration + + +abstract - - + + struct_member_declaration - - -private + + +literal - - -overloadable_binary_operator + + +) - - -contextual_keyword + + +( - - -false + + +second - - -primary_constraint + + +; - - -value + + +} + + + +where + + + +public + + + +indexer_modifier + + + +fixed_parameter + + + +attribute_name + + + +struct + + + +identifier + + + +attribute_list + + + +{ - - -literal + + +expression + + + +event identifier - - -explicitly_typed_local_variable_declaration + + +int - - -{ + + +public - - -interface_modifier + + +primary_expression - - -identifier + + +} - - -A + + +{ - - -int + + +struct_member_declaration - - -protected + + +block - - -Value + + +accessor_body - - -property_declaration + + +field_modifier - - -field_declaration + + +} - - -[ + + +contextual_keyword - - -{ + + +class_type - - -int + + +get_accessor_declaration - - -identifier + + +type_parameter_constraints_clause - - -constructor_declarator + + +integral_type - - -type + + +protected - - -attribute_argument_expression + + +volatile - - -( + + +class_type - - -0 + + +compilation_unit - - -interface_method_header + + +method_declaration - - -boolean_literal + + +get_accessor_declaration - - -public + + +get - - -value + + +identifier - - -T + + +; - - -Add + + +struct_member_declaration - - -explicitly_typed_local_variable_declarators + + +S - - -} + + +static - - -struct_member_declaration + + +) - - -get_accessor_declaration + + +type_parameter_constraints - - -public + + +class_body - - -identifier + + +1 - - -expression + + +this - - -( + + +local_variable_initializer - - -type_parameter_constraints_clause + + +fixed_parameter - - -} + + +"A" - - -event_declaration + + +method_modifiers - - -) + + +; - - -struct_member_declaration + + +interface_member_declaration - - -class + + +Obsolete - - -type + + +literal - - -{ + + +primary_expression - - -return_statement + + +type - - -first + + +parameter_list - - -identifier + + +block member_access - - -get + + +[ + + + +class_type - - -; + + +int - - -identifier + + +parameter_list - - -accessor_declarations + + +T - - -{ + + +attribute - - -"Use·Script·instead" + + +explicitly_typed_local_variable_declarator - - -type + + +0 - - -P + + +struct_interfaces - - + + identifier - - -statement_list - - - -abstract + + +argument_list - - -A + + +return - - -statement_list + + +local_variable_declaration - - -; + + +string - - + + type - - -P - - - -identifier + + +int - - -constructor_declaration + + +integral_type - - -type + + +binary_operator_declarator - - -interface_type_list + + +( - - -fixed_size_buffer_declarators + + +identifier index - - -> - - - -return_statement - - - -public - - - -namespace_member_declaration + + +boolean_literal - - -identifier + + +property_body - - -positional_argument + + +ref_method_modifier - - -statement + + +struct_member_declaration - - + + int - - -attribute_list + + +prog - - -constant_expression + + +struct_declaration - - -namespace_body + + +fixed_size_buffer_declarator - - -class_declaration + + +> - - -I + + +namespace_member_declaration - - -accessor_body + + +; - - -return_statement + + +property_modifier - - -struct_member_declaration + + +} - - -expression + + +set_accessor_declaration - - -identifier + + +struct_member_declaration - - -return + + +; - - -void + + +invocation_expression - - + + int - - -first - - - -identifier - - - -literal - - - -attribute_arguments - - - -method_body + + +integral_type - - -fixed_parameter + + +fixed_size_buffer_declaration - - -interface + + +{ - - -} + + +member_name - - -class_body + + +namespace_member_declaration - - -variable_declarators + + +{ - - -< + + +interface_declaration positional_argument_list - - -local_variable_initializer + + +identifier - - -method_header + + +class - - -accessor_body + + +E - - -public + + +P - - -get + + +Event - - -accessor_body + + +identifier - - -; + + +identifier - - -fixed_parameter + + +integral_type - - -set_accessor_declaration + + +string - - -explicitly_typed_local_variable_declarator + + +property_body - - -fixed_parameter + + +qualified_identifier - - -= + + +identifier - - -member_name + + +T - - -identifier + + +{ - - -interface_member_declaration + + +t - - -type + + +explicitly_typed_local_variable_declaration - - -[ + + +accessor_body - - -second + + +identifier - - -attribute + + +integral_type - - -declaration_statement + + +operator_declarator - - -} + + +) - - -fixed + + +type - - -{ + + +namespace_body - - -interface_declaration + + +positional_argument - - -public + + +[ - - -, + + +My - - -t + + +error - - -abstract + + +type - - -f1 + + +get - - -field_declaration + + +return - - -method_modifier + + +type - - -interface_body + + +void - - -struct_declaration + + +type - - -decorated_type_parameter + + +interface_body - - -expression + + +namespace - - -; + + +constructor_declaration + + + +I - - + + type - - -field + + +set - - -field_modifier + + +struct_body - - -T + + +interface_type_list - - -variable_declarators + + +fixed_parameter - - -type_parameter_list + + +public - - + + +: + + + +false + + + +parameter_list + + + ; - - -identifier + + +{ + + + += - - -) + + +identifier - - -type_parameter + + +public - - -set + + +member_name - - -parameter_list + + +interface - - -field_modifier + + +literal - - -struct_member_declaration + + +accessor_body - - -namespace + + +{ - - -struct_member_declaration + + +member_name - - + + ; - - -block - - - -property_modifier + + +first - - -identifier + + +f2 - - -C + + +public - - -identifier + + +; - - -constructor_body + + +type - - -I + + +T - - -My + + +fixed - - -accessor_body + + +} - - -binary_operator_declarator + + +first - - -struct_member_declaration + + +property_declaration - - -accessor_declarations + + +A - - -type_parameter_constraints + + +method_header - - -; + + +method_body - - + + identifier - - -primary_expression - - - -"A" + + +) - - -. + + +int - - -prog + + +accessor_modifier - - -A + + +block - - -return_type + + +return_statement - - -public + + +get - - -S + + +{ - - -get_accessor_declaration + + +class_declaration - - -get + + +identifier - - -fixed_size_buffer_declaration + + +; - - -second + + +statement_list - - -block + + +operator_modifier - - -string + + +accessor_declarations - - -fixed_parameter + + +identifier - - -T + + +identifier - - -argument_list + + +constructor_declarator - - -attribute_section + + +attribute_argument_expression - - -attributes + + +return_statement - - -; + + +{ - - -operator_declaration + + +public - - -internal + + +primary_constraint - - -type + + +second \ No newline at end of file From 3d198d44095a5743d47a9eff5ea72c25fc1678f4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 25 Sep 2025 13:56:40 -0400 Subject: [PATCH 35/47] mostly grammar edits Edit files other than interfaces post meeting --- standard/basic-concepts.md | 12 ++++++------ standard/classes.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 9428b90bb..d2c42ed7d 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -78,7 +78,7 @@ There are several different types of declaration spaces, as described in the fol - Within all compilation units of a program, *namespace_member_declaration*s with no enclosing *namespace_declaration* are members of a single combined declaration space called the ***global declaration space***. - Within all compilation units of a program, *namespace_member_declaration*s within *namespace_declaration*s that have the same fully qualified namespace name are members of a single combined declaration space. - Each *compilation_unit* and *namespace_body* has an ***alias declaration space***. Each *extern_alias_directive* and *using_alias_directive* of the *compilation_unit* or *namespace_body* contributes a member to the alias declaration space ([§14.5.2](namespaces.md#1452-using-alias-directives)). -- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§16.2.4](structs.md#1624-partial-modifier)). Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class, interface, or struct cannot contain a member declaration with the same name as the class, interface, or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. +- Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ([§16.2.4](structs.md#1624-partial-modifier)). Names are introduced into this declaration space through *class_member_declaration*s, *struct_member_declaration*s, *interface_member_declaration*s, or *type_parameter*s. Except for overloaded instance constructor declarations and static constructor declarations, a class, struct, or interface cannot contain a member declaration with the same name as the class, struct, or interface. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to ***hide*** the inherited member. - Each delegate declaration creates a new declaration space. Names are introduced into this declaration space through parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. - Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through *enum_member_declarations*. - Each method declaration, property declaration, property accessor declaration, indexer declaration, indexer accessor declaration, operator declaration, instance constructor declaration, anonymous function, and local function creates a new declaration space called a ***local variable declaration space***. Names are introduced into this declaration space through parameters (*fixed_parameter*s and *parameter_array*s) and *type_parameter*s. The set accessor for a property or an indexer introduces the name `value` as a parameter. The body of the function member, anonymous function, or local function, if any, is considered to be nested within the local variable declaration space. When a local variable declaration space and a nested local variable declaration space contain elements with the same name, within the scope of the nested local name, the outer local name is hidden ([§7.7.1](basic-concepts.md#771-general)) by the nested local name. @@ -781,9 +781,9 @@ When a name in an inner scope hides a name in an outer scope, it hides all overl Name hiding through inheritance occurs when classes or structs redeclare names that were inherited from base classes. This type of name hiding takes one of the following forms: -- A constant, field, property, event, or type introduced in a class, interface, or struct hides all base class members with the same name. -- A method introduced in a class, interface, or struct hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). -- An indexer introduced in a class, interface, or struct hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . +- A constant, field, property, event, or type introduced in a class, struct, or interface hides all base class members with the same name. +- A method introduced in a class, struct, or interface hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). +- An indexer introduced in a class, struct, or interface hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . The rules governing operator declarations ([§15.10](classes.md#1510-operators)) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another. @@ -913,7 +913,7 @@ where: `R₀` is determined as follows: - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§15.6](classes.md#156-methods)) but outside the *attributes* of its *method-header*, and if that declaration includes a type parameter ([§15.2.3](classes.md#1523-type-parameters)) with name `I`, then `R₀` refers to that type parameter. -- Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class, interface, or struct declaration (if any): +- Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class, struct, or interface declaration (if any): - If `x` is zero and the declaration of `T` includes a type parameter with name `I`, then `R₀` refers to that type parameter. - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. > *Note*: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the *namespace_or_type_name*. *end note* @@ -945,7 +945,7 @@ For each repetition `n`, where `1 ≤ n ≤ k`, its resolution, `Rₙ`; which in - If `x` is zero and `Rₚ` refers to a namespace and `Rₚ` contains a nested namespace with name `I`, then `Rₙ` refers to that nested namespace. - Otherwise, if `Rₚ` refers to a namespace and `Rₚ` contains an accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. -- Otherwise, if `Rₚ` refers to a (possibly constructed) class, interface, or struct type and `Rₚ` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. +- Otherwise, if `Rₚ` refers to a (possibly constructed) class, struct, or interface type and `Rₚ` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. > *Note*: If the meaning of `T.I`, for some type `T`, is being determined as part of resolving the base class specification of `T` then the direct base class of `T` is considered to be `object` ([§15.2.4.2](classes.md#15242-base-classes)). *end note* - Otherwise, the *namespace_or_type_name* is invalid and a compile-time error occurs. diff --git a/standard/classes.md b/standard/classes.md index dec7b8526..792bfaea0 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1098,7 +1098,7 @@ When a field, method, property, event, indexer, constructor, or finalizer declar #### 15.3.9.1 General -A type declared within a class, interface, or struct is called a ***nested type***. A type that is declared within a compilation unit or namespace is called a ***non-nested type***. +A type declared within a class, struct, or interface is called a ***nested type***. A type that is declared within a compilation unit or namespace is called a ***non-nested type***. > *Example*: In the following example: > @@ -1126,7 +1126,7 @@ The fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names) #### 15.3.9.3 Declared accessibility -Non-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, interface, or struct: +Non-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, struct, or interface: - A nested type that is declared in a class or interface can have any of the permitted kinds of declared accessibility and, like other class members, defaults to `private` declared accessibility. - A nested type that is declared in a struct can have any of three forms of declared accessibility (`public`, `internal`, or `private`) and, like other struct members, defaults to `private` declared accessibility. From a2302e67e5201948d77151906f10fed252ca5b6f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 25 Sep 2025 17:35:54 -0400 Subject: [PATCH 36/47] Updates from review at 9/25 meeting Updates terminology from 'override' to 'implementation' for interface members, clarifies rules for hiding and explicitly implementing inherited members, and improves examples and notes for interface member access and inheritance. Adds details about the use of the 'new' modifier and compiler warnings, and refines explanations of most specific implementation in diamond inheritance scenarios. --- standard/interfaces.md | 82 +++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 8bb8a3b02..ae297531d 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -6,6 +6,7 @@ An interface defines a contract. A class or struct that implements an interface Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. + > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* ## 19.2 Interface declarations @@ -129,6 +130,8 @@ A type `T` is variance-convertible to a type `T *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§19.4](interfaces.md#194-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -> *Example*: Consider an interface `IA` with an implementation for a member `M`. As `M` is not abstract, outside that interface or any interface derived from it, that name is not visible. It must be accessed through a reference whose compile-time type is an interface that is implicitly convertible to `IA`. +> *Example*: Consider an interface `IA` with an implementation for a member `M` and a property `P`. An implementing type `C` doesn't provide an implementation for either `M` or `P`. They must be accessed through a reference whose compile-time type is an interface that is implicitly convertible to `IA` of `IB`. These members aren't found through member lookup on a variable of type `C`. > > > ```csharp @@ -290,11 +291,7 @@ The set of members of an interface declared in multiple parts ([§15.2.7](classe > } > ``` > ->Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Main`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. *end example* - - - -> *Note*: See how the declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end note* +>Within the interfaces `IA` and `IB`, member `M` is accessible directly by name. However, within method `Main`, we cannot write `c.M()` or `c.P`, as those names are not visible. To find them, casts to the appropriate interface type are needed. The declaration of `M` in `IB` uses explicit interface implementation syntax. This is necessary to make that method override the one in `IA`; the modifier `override` may not be applied to a function member. *end example* ### §interface-fields Interface fields @@ -351,7 +348,8 @@ This clause augments the description of methods in classes [§15.6](classes.md#1 Interface properties are declared using *method_declaration*s (§15.6)). The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules: - *method_modifier* shall not include `override`. -- An interface method declaration that has a block body or expression body as a *method_body* has an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. +- A method whose body is a semi-colon (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. +- An interface method declaration that has a block body or expression body as a *method_body* has is `virtual`; the `virtual` modifier is not required, but is allowed. - A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. - The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: - A static declaration that is not extern shall have a block body or expression body as a *method_body*. @@ -437,12 +435,13 @@ Interface properties are declared using *property_declaration*s ([§15.7.1](clas - *property_modifier* shall not include `override`. - An explicit interface member implementation shall not contain an *accessor_modifier* (§15.7.3). -- A derived interface may implement an abstract interface property declared in a base interface. +- A derived interface may explicitly implement an abstract interface property declared in a base interface. > *Note*: As an interface cannot contain instance fields, an interface property cannot be an instance auto-property, as that would require the declaration of implicit hidden instance fields. *end note* - The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor. -- A *property_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. An instance *property_declaration* that has no implementation is always considered abstract; it is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). +- An interface method declaration that has a block body or expression body as a *method_body* is `virtual`; the `virtual` modifier is not required, but is allowed. +- An instance *property_declaration* that has no implementation is `abstract`; the `abstract` modifier is not required, but is allowed. It is *never* considered to be an automatically implemented property ([§15.7.4](classes.md#1574-automatically-implemented-properties)). ### 19.4.4 Interface events @@ -464,7 +463,8 @@ This clause augments the description of indexers in classes [§15.9](classes.md# Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. -- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is an implementation ([§19.1](interfaces.md#191-general)), so it is *not* abstract. +- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is `virtual`; the `virtual` modifier is not required, but is allowed. +- An *indexer_declaration* whose accessors are semi-colons (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. - All the parameter types of an interface indexer shall be input-safe ([§19.2.3.2](interfaces.md#19232-variance-safety)). - Any output or reference parameter types shall also be output-safe. @@ -484,11 +484,11 @@ It is a compile-time error for an interface to declare a conversion, equality, o This clause augments the description of static constructors in classes [§15.12](classes.md#1512-static-constructors) for static constructors declared in interfaces. -The static constructor for a closed interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: +The static constructor for a closed (§8.4.3) interface executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following actions to occur within an application domain: - Any of the static members of the interface are referenced. -- Before the Main method is called for an interface containing the Main method ([§7.1](basic-concepts.md#71-application-startup)) in which execution begins. -- The implementation for a member defined in the interface is accessed as the most specific implementation (§most-specific-implementation) for that member. +- Before the `Main` method is called for an interface containing the `Main` method ([§7.1](basic-concepts.md#71-application-startup)) in which execution begins. +- That interface provides an implementation for a member, and that implementation is accessed as the most specific implementation (§most-specific-implementation) for that member. > *Note*: In the case where none of the preceding actions take place, the static constructor for an interface may not execute for a program where instances of types that implement the interface are created and used. *end note* @@ -514,16 +514,16 @@ It is an error to declare a class type, struct type, or enum type within the sco > > *end example* -### §most-specific-implementation Most specific override +### §most-specific-implementation most specific implementation -Every interface and class shall have a most specific override for every virtual member declared in all interfaces implemented by that type among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific override*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific override. +Every class and struct shall have a most specific implementation for every virtual member declared in all interfaces implemented by that type among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific implementation*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific implementation. -> *Note*: The most specific override rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* +> *Note*: The most specific implementation rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* -For a type `T` that is a struct or a class that implements interfaces `I2` and `I3`, where `I2` and `I3` both derive directly or indirectly from interface `I` that declares a member `M`, the most specific override of `M` is: +For a type `T` that is a struct or a class that implements interfaces `I2` and `I3`, where `I2` and `I3` both derive directly or indirectly from interface `I` that declares a member `M`, the most specific implementation of `M` is: -- If `T` declares an implementation of `I.M`, that implementation is the most specific override. -- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific override. +- If `T` declares an implementation of `I.M`, that implementation is the most specific implementation. +- Otherwise, if `T` is a class and a direct or indirect base class declares an implementation of `I.M`, the most derived base class of `T` is the most specific implementation. - Otherwise, if `I2` and `I3` are interfaces implemented by `T` and `I3` derives from `I2` either directly or indirectly, `I3.M` is a more specific implementation than `I2.M`. - Otherwise, neither `I2.M` nor `I3.M` are more specific and an error occurs. @@ -546,7 +546,7 @@ For a type `T` that is a struct or a class that implements interfaces `I2` and ` > void IA.M() { Console.WriteLine("IC.M"); } > } > -> abstract class C: IB, IC { } // error: no most specific override for 'IA.M' +> abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' > > abstract class D: IA, IB, IC // OK > { @@ -554,33 +554,13 @@ For a type `T` that is a struct or a class that implements interfaces `I2` and ` > } > ``` > -> The most specific override rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* - -It is an error if in a class declaration the most specific override of some interface method is an abstract override that was declared in a class or an interface. - -> *Example*: -> -> -> ```csharp -> interface IF -> { -> void M(); -> } -> -> abstract class F : IF { } // error: 'F' does not implement 'IF.M' -> ``` -> -> *end example* +> The most specific implementation rule ensures that a conflict (i.e., an ambiguity arising from diamond inheritance) is resolved explicitly by the programmer at the point where the conflict arises. *end example* ### 19.4.6 Interface member access Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. -In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`: - -- The expression `base.M()` is valid only if `B` provides a definition for `M`. The expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M` is in a class type. To access the most specific implementation if it was defined in an interface, use the cast expression `(this as I).M()` to refer to the most specific implementation (§most-specific-implementation) of `M` in `I` or an interface derived from `I`. -- If `B` contains a method `M` without the `virtual` or `override` modifier, that method hides `I.M()` rather than overriding it. -- If `B` contains a method `M` with a different parameter list, that method `M` is a distinct overload of `M`. +In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`, the expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M()` is in a class type. For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. @@ -808,7 +788,9 @@ The base interfaces of a generic class declaration shall satisfy the uniqueness ### 19.6.2 Explicit interface member implementations -For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. +For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. An interface that implements a member in a base interface must declare an explicit interface member implementation. + +A derived interface member that satisfies interface mapping (§19.6.5) hides the base interface member (§7.7.2). The compiler shall issue a warning unless the `new` modifier is present. > *Example*: > From 9f75cf4622590921a6a7b6461732f93d57bfa336 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 25 Sep 2025 17:38:28 -0400 Subject: [PATCH 37/47] lint --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index ae297531d..21f9193c1 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -788,7 +788,7 @@ The base interfaces of a generic class declaration shall satisfy the uniqueness ### 19.6.2 Explicit interface member implementations -For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. An interface that implements a member in a base interface must declare an explicit interface member implementation. +For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. An interface that implements a member in a base interface must declare an explicit interface member implementation. A derived interface member that satisfies interface mapping (§19.6.5) hides the base interface member (§7.7.2). The compiler shall issue a warning unless the `new` modifier is present. From 7b843a2e614a025fd8a5eac9830ebaa2b3f7d7bf Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 26 Sep 2025 13:20:10 -0400 Subject: [PATCH 38/47] Address review comments Address review comments from Nigel on 9/25-26 --- standard/basic-concepts.md | 10 +++++----- standard/classes.md | 2 +- standard/interfaces.md | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index d2c42ed7d..e8a8e847b 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -268,7 +268,7 @@ When access to a particular member is allowed, the member is said to be ***acces The ***declared accessibility*** of a member can be one of the following: - Public, which is selected by including a `public` modifier in the member declaration. The intuitive meaning of `public` is “access not limited”. -- Protected, which is selected by including a `protected` modifier in the member declaration. The intuitive meaning of `protected` is “access limited to the containing class or types derived from the containing class”. +- Protected, which is selected by including a `protected` modifier in the member declaration. The intuitive meaning of `protected` is “access limited to the containing type or types derived from the containing type”. - Internal, which is selected by including an `internal` modifier in the member declaration. The intuitive meaning of `internal` is “access limited to this assembly”. - Protected internal, which is selected by including both a `protected` and an `internal` modifier in the member declaration. The intuitive meaning of `protected internal` is “accessible within this assembly as well as types derived from the containing class”. - Private protected, which is selected by including both a `private` and a `protected` modifier in the member declaration. The intuitive meaning of `private protected` is “accessible within this assembly by the containing class and types derived from the containing class.” @@ -402,7 +402,7 @@ As described in [§7.4](basic-concepts.md#74-members), all members of a base cla ### 7.5.4 Protected access -When a `protected` or `private protected` instance member is accessed outside the program text of the class in which it is declared, and when a `protected internal` instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place *through* an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class. +When a `protected` or `private protected` instance member is accessed outside the program text of the class in which it is declared, and when a `protected internal` instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place *through* an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class. Instance interface members defined with `protected` or `private protected` access cannot be accessed from a `class` or `struct` that implements that interface; these can be accessed only from derived interfaces. However, `class` and `struct` types can override `protected` instance members declared in an interface it implements. Let `B` be a base class that declares a protected instance member `M`, and let `D` be a class that derives from `B`. Within the *class_body* of `D`, access to `M` can take one of the following forms: @@ -783,7 +783,7 @@ Name hiding through inheritance occurs when classes or structs redeclare names t - A constant, field, property, event, or type introduced in a class, struct, or interface hides all base class members with the same name. - A method introduced in a class, struct, or interface hides all non-method base class members with the same name, and all base class methods with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)). -- An indexer introduced in a class, struct, or interface hides all base class indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . +- An indexer introduced in a class, struct, or interface hides all base type indexers with the same signature ([§7.6](basic-concepts.md#76-signatures-and-overloading)) . The rules governing operator declarations ([§15.10](classes.md#1510-operators)) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another. @@ -915,7 +915,7 @@ where: - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§15.6](classes.md#156-methods)) but outside the *attributes* of its *method-header*, and if that declaration includes a type parameter ([§15.2.3](classes.md#1523-type-parameters)) with name `I`, then `R₀` refers to that type parameter. - Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class, struct, or interface declaration (if any): - If `x` is zero and the declaration of `T` includes a type parameter with name `I`, then `R₀` refers to that type parameter. - - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. + - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others, as in the case if nested types declared in multiple interfaces. > *Note*: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the *namespace_or_type_name*. *end note* - Otherwise, for each namespace `N`, starting with the namespace in which the *namespace_or_type_name* occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located: - If `x` is zero and `I` is the name of a namespace in `N`, then: @@ -945,7 +945,7 @@ For each repetition `n`, where `1 ≤ n ≤ k`, its resolution, `Rₙ`; which in - If `x` is zero and `Rₚ` refers to a namespace and `Rₚ` contains a nested namespace with name `I`, then `Rₙ` refers to that nested namespace. - Otherwise, if `Rₚ` refers to a namespace and `Rₚ` contains an accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. -- Otherwise, if `Rₚ` refers to a (possibly constructed) class, struct, or interface type and `Rₚ` or any of its base classes contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. +- Otherwise, if `Rₚ` refers to a (possibly constructed) class, struct, or interface type and `Rₚ` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others, as in the case if nested types declared in multiple interfaces. > *Note*: If the meaning of `T.I`, for some type `T`, is being determined as part of resolving the base class specification of `T` then the direct base class of `T` is considered to be `object` ([§15.2.4.2](classes.md#15242-base-classes)). *end note* - Otherwise, the *namespace_or_type_name* is invalid and a compile-time error occurs. diff --git a/standard/classes.md b/standard/classes.md index 792bfaea0..ad8eef8aa 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -1128,7 +1128,7 @@ The fully qualified name ([§7.8.3](basic-concepts.md#783-fully-qualified-names) Non-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, struct, or interface: -- A nested type that is declared in a class or interface can have any of the permitted kinds of declared accessibility and, like other class members, defaults to `private` declared accessibility. +- A nested type that is declared in a class can have any of the permitted kinds of declared accessibility and, like other class members, defaults to `private` declared accessibility. - A nested type that is declared in a struct can have any of three forms of declared accessibility (`public`, `internal`, or `private`) and, like other struct members, defaults to `private` declared accessibility. > *Example*: The example diff --git a/standard/interfaces.md b/standard/interfaces.md index 21f9193c1..9e551e5e7 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by derived interfaces or by classes or structs that implement the interface. +Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by classes or structs that implement the interface, or derived interface that provide an overriding definition. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* @@ -349,7 +349,7 @@ Interface properties are declared using *method_declaration*s (§15.6)). The *at - *method_modifier* shall not include `override`. - A method whose body is a semi-colon (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. -- An interface method declaration that has a block body or expression body as a *method_body* has is `virtual`; the `virtual` modifier is not required, but is allowed. +- An interface method declaration that has a block body or expression body as a *method_body* is `virtual`; the `virtual` modifier is not required, but is allowed. - A *method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*. - The list of requirements for valid combinations of modifiers stated for a class method is extended, as follows: - A static declaration that is not extern shall have a block body or expression body as a *method_body*. From c4c511ba7d549f2722018e92d7b99009161d0b20 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 29 Sep 2025 16:06:32 -0400 Subject: [PATCH 39/47] Apply suggestions from code review Co-authored-by: Joseph Musser --- standard/interfaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 9e551e5e7..47fda3a55 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -255,7 +255,7 @@ If a `new` modifier is included in a declaration that doesn’t hide an inherite The set of members of an interface declared in multiple parts ([§15.2.7](classes.md#1527-partial-type-declarations)) is the union of the members declared in each part. The bodies of all parts of the interface declaration share the same declaration space ([§7.3](basic-concepts.md#73-declarations)), and the scope of each member ([§7.7](basic-concepts.md#77-scopes)) extends to the bodies of all the parts. -> *Example*: Consider an interface `IA` with an implementation for a member `M` and a property `P`. An implementing type `C` doesn't provide an implementation for either `M` or `P`. They must be accessed through a reference whose compile-time type is an interface that is implicitly convertible to `IA` of `IB`. These members aren't found through member lookup on a variable of type `C`. +> *Example*: Consider an interface `IA` with an implementation for a member `M` and a property `P`. An implementing type `C` doesn't provide an implementation for either `M` or `P`. They must be accessed through a reference whose compile-time type is an interface that is implicitly convertible to `IA` or `IB`. These members aren't found through member lookup on a variable of type `C`. > > > ```csharp @@ -516,7 +516,7 @@ It is an error to declare a class type, struct type, or enum type within the sco ### §most-specific-implementation most specific implementation -Every class and struct shall have a most specific implementation for every virtual member declared in all interfaces implemented by that type among the overrides appearing in the type or its direct and indirect interfaces. The ***most specific implementation*** is a unique override that is more specific than every other override. If there is no override, the member itself is considered the most specific implementation. +Every class and struct shall have a most specific implementation for every virtual member declared in all interfaces implemented by that type among the implementations appearing in the type or its direct and indirect interfaces. The ***most specific implementation*** is a unique implementation that is more specific than every other implementation. > *Note*: The most specific implementation rule ensures that an ambiguity arising from diamond interface inheritance is resolved explicitly by the programmer at the point where the conflict occurs. *end note* From 709682ece4ac3d257fa3b5e284a4018d7b49920c Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 30 Sep 2025 16:11:04 -0400 Subject: [PATCH 40/47] Edits per review Respond to edits except for the rabbit hole opened by https://github.com/dotnet/csharpstandard/pull/681/files#r2386725345 --- standard/basic-concepts.md | 30 +++++++++++++++++++++++++++--- standard/interfaces.md | 8 ++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index e8a8e847b..7c908ad46 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -268,7 +268,7 @@ When access to a particular member is allowed, the member is said to be ***acces The ***declared accessibility*** of a member can be one of the following: - Public, which is selected by including a `public` modifier in the member declaration. The intuitive meaning of `public` is “access not limited”. -- Protected, which is selected by including a `protected` modifier in the member declaration. The intuitive meaning of `protected` is “access limited to the containing type or types derived from the containing type”. +- Protected, which is selected by including a `protected` modifier in the member declaration. The intuitive meaning of `protected` is “access limited to the containing class or interface, or classes or interfaces derived from the containing type”. - Internal, which is selected by including an `internal` modifier in the member declaration. The intuitive meaning of `internal` is “access limited to this assembly”. - Protected internal, which is selected by including both a `protected` and an `internal` modifier in the member declaration. The intuitive meaning of `protected internal` is “accessible within this assembly as well as types derived from the containing class”. - Private protected, which is selected by including both a `private` and a `protected` modifier in the member declaration. The intuitive meaning of `private protected` is “accessible within this assembly by the containing class and types derived from the containing class.” @@ -915,7 +915,7 @@ where: - If `x` is zero and the *namespace_or_type_name* appears within a generic method declaration ([§15.6](classes.md#156-methods)) but outside the *attributes* of its *method-header*, and if that declaration includes a type parameter ([§15.2.3](classes.md#1523-type-parameters)) with name `I`, then `R₀` refers to that type parameter. - Otherwise, if the *namespace_or_type_name* appears within a type declaration, then for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of that type declaration and continuing with the instance type of each enclosing class, struct, or interface declaration (if any): - If `x` is zero and the declaration of `T` includes a type parameter with name `I`, then `R₀` refers to that type parameter. - - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others, as in the case if nested types declared in multiple interfaces. + - Otherwise, if the *namespace_or_type_name* appears within the body of the type declaration, and `T` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `R₀` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others. > *Note*: Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the *namespace_or_type_name*. *end note* - Otherwise, for each namespace `N`, starting with the namespace in which the *namespace_or_type_name* occurs, continuing with each enclosing namespace (if any), and ending with the global namespace, the following steps are evaluated until an entity is located: - If `x` is zero and `I` is the name of a namespace in `N`, then: @@ -945,7 +945,7 @@ For each repetition `n`, where `1 ≤ n ≤ k`, its resolution, `Rₙ`; which in - If `x` is zero and `Rₚ` refers to a namespace and `Rₚ` contains a nested namespace with name `I`, then `Rₙ` refers to that nested namespace. - Otherwise, if `Rₚ` refers to a namespace and `Rₚ` contains an accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. -- Otherwise, if `Rₚ` refers to a (possibly constructed) class, struct, or interface type and `Rₚ` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others, as in the case if nested types declared in multiple interfaces. +- Otherwise, if `Rₚ` refers to a (possibly constructed) class, struct, or interface type and `Rₚ` or any of its base types contain a nested accessible type having name `I` and `x` type parameters, then `Rₙ` refers to that type constructed with the given type arguments. If there is more than one such type, the type declared within the more derived type is selected. It is a compiler error if no type is more derived than all others. > *Note*: If the meaning of `T.I`, for some type `T`, is being determined as part of resolving the base class specification of `T` then the direct base class of `T` is considered to be `object` ([§15.2.4.2](classes.md#15242-base-classes)). *end note* - Otherwise, the *namespace_or_type_name* is invalid and a compile-time error occurs. @@ -956,6 +956,30 @@ A *namespace_or_type_name* is permitted to reference a static class ([§15.2.2.4 - The *namespace_or_type_name* is the `T` in a *namespace_or_type_name* of the form `T.I`, or - The *namespace_or_type_name* is the `T` in a *typeof_expression* ([§12.8.18](expressions.md#12818-the-typeof-operator)) of the form `typeof(T)` +> *Example*: +> +> +> ```csharp +> class A +> { +> class NestedClass { public static void M() {} } +> } +> +> interface B +> { +> class NestedClass { public static void M() {} } +> } +> +> interface C : A, B +> { +> public void Test() { NestedClass.M(); } // ambiguity between A.NestedClass and B.NestedClass +> } +> ``` +> +> In the example above, the call to `NestedClass.M()` in `C.Test()` is ambiguous between `B.NestedClass.M()` and `A.NestedClass.M()` because neither is more derived than the other. An explicit reference to either `A.NestedClass.M()` or `B.NestedClass.M()` is required to resolve the ambiguity. +> +> *end example* + ### 7.8.2 Unqualified names Every namespace declaration and type declaration has an ***unqualified name*** determined as follows: diff --git a/standard/interfaces.md b/standard/interfaces.md index 47fda3a55..dbf9093c3 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -345,7 +345,7 @@ See §interface-static-constructors for information regarding the allocation and This clause augments the description of methods in classes [§15.6](classes.md#156-methods) for methods declared in interfaces. -Interface properties are declared using *method_declaration*s (§15.6)). The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules: +Interface methods are declared using *method_declaration*s (§15.6)). The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class. Interface methods have the following additional rules: - *method_modifier* shall not include `override`. - A method whose body is a semi-colon (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. @@ -463,8 +463,8 @@ This clause augments the description of indexers in classes [§15.9](classes.md# Interface indexers are declared using *indexer_declaration*s ([§15.9](classes.md#159-indexers)), with the following additional rules: - *indexer_modifier* shall not include `override`. -- An *indexer_declaration* that has an *expression body* or contains an accessor with a body is `virtual`; the `virtual` modifier is not required, but is allowed. -- An *indexer_declaration* whose accessors are semi-colons (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. +- An *indexer_declaration* that has an *expression body* or contains an accessor with a block body or expression body is `virtual`; the `virtual` modifier is not required, but is allowed. +- An *indexer_declaration* whose accessor bodies are semi-colons (`;`) is `abstract`; the `abstract` modifier is not required, but is allowed. - All the parameter types of an interface indexer shall be input-safe ([§19.2.3.2](interfaces.md#19232-variance-safety)). - Any output or reference parameter types shall also be output-safe. @@ -560,7 +560,7 @@ For a type `T` that is a struct or a class that implements interfaces `I2` and ` Interface members are accessed through member access ([§12.8.7](expressions.md#1287-member-access)) and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) expressions of the form `I.M` and `I[A]`, where `I` is an interface type, `M` is a constant, field, method, property, or event of that interface type, and `A` is an indexer argument list. -In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`, the expression `base.M()` is valid only if the most specific implementation (§most-specific-implementation) of `M()` is in a class type. +In a class `D`, with direct or indirect base class `B`, where `B` directly or indirectly implements interface `I` and `I` defines a method `M()`, the expression `base.M()` is valid only if `base.M()` staticly (§12.3) binds to an implementation of `M()` in a class type. For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup ([§12.5](expressions.md#125-member-lookup)), method invocation ([§12.8.10.2](expressions.md#128102-method-invocations)), and indexer access ([§12.8.12.4](expressions.md#128124-indexer-access)) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This subclause shows several examples, some of which lead to ambiguities and others which don’t. In all cases, explicit casts can be used to resolve the ambiguities. From 04150a2be8b50e3c8c1daa7442de288bd4fc917c Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 30 Sep 2025 16:15:47 -0400 Subject: [PATCH 41/47] fix test --- standard/basic-concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 7c908ad46..41c915a96 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -958,7 +958,7 @@ A *namespace_or_type_name* is permitted to reference a static class ([§15.2.2.4 > *Example*: > -> +> > ```csharp > class A > { From ef92d6e494671b7060e5be13510f57e418dbe86a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 2 Oct 2025 09:19:43 -0400 Subject: [PATCH 42/47] fix build issue. --- standard/basic-concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 41c915a96..87e7b3b32 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -958,7 +958,7 @@ A *namespace_or_type_name* is permitted to reference a static class ([§15.2.2.4 > *Example*: > -> +> > ```csharp > class A > { From acfdffe5b1efa6c0a40cce28ca203ad225315da0 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 2 Oct 2025 10:59:26 -0400 Subject: [PATCH 43/47] Update rules for lookup and mapping Update rules for interface member lookup and mapping (or implementing) an interface member. Add C# 10 note For our future selves. --- standard/basic-concepts.md | 2 +- standard/interfaces.md | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 87e7b3b32..faf0f1a5c 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -960,7 +960,7 @@ A *namespace_or_type_name* is permitted to reference a static class ([§15.2.2.4 > > > ```csharp -> class A +> interface A > { > class NestedClass { public static void M() {} } > } diff --git a/standard/interfaces.md b/standard/interfaces.md index dbf9093c3..5b6186a76 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -4,7 +4,7 @@ An interface defines a contract. A class or struct that implements an interface shall adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces. -Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations are supplied by classes or structs that implement the interface, or derived interface that provide an overriding definition. +Interfaces may contain various kinds of members, as described in [§19.4](interfaces.md#194-interface-members). The interface itself may provide an implementation for some or all of the function members that it declares. Members for which the interface does not provide an implementation are abstract. Their implementations must be supplied by classes or structs that implement the interface, or derived interface that provide an overriding definition. > *Note*: Historically, adding a new function member to an interface impacted all existing consumers of that interface type; it was a breaking change. The addition of interface function member implementations allowed developers to upgrade an interface while still enabling any implementors to override that implementation. Users of the interface can accept the implementation as a non-breaking change; however, if their requirements are different, they can override the provided implementations. *end note* @@ -718,6 +718,10 @@ When an interface is part of a namespace, a qualified interface member name can Interfaces may be implemented by classes and structs. To indicate that a class or struct directly implements an interface, the interface is included in the base class list of the class or struct. +A class or struct `C` that implements an interface `I` must provide or inherit an implementation for every member declared in `I`. Public members of `I` may be defined in public members of `C`. Non-public members declared in `I` that are accessible in `C` may be defined in `C` using explicit interface implmentation (§19.6.2). Non-accessible members, such as `private` members of `I` can't be defined in `C`. + + A member in a derived type that satisfies interface mapping (§19.6.5) but does not implement the matching base interface member introduces a new member. This occurs when explicit interface implementation is required to define the interface member. + > *Example*: > > @@ -788,7 +792,8 @@ The base interfaces of a generic class declaration shall satisfy the uniqueness ### 19.6.2 Explicit interface member implementations -For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. An interface that implements a member in a base interface must declare an explicit interface member implementation. + +For purposes of implementing interfaces, a class, struct, or interface may declare ***explicit interface member implementations***. An explicit interface member implementation is a method, property, event, or indexer declaration that references a qualified interface member name. A class or struct that implements a non-public member in a base interface must declare an explicit interface member implementation. An interface that implements a member in a base interface must declare an explicit interface member implementation. A derived interface member that satisfies interface mapping (§19.6.5) hides the base interface member (§7.7.2). The compiler shall issue a warning unless the `new` modifier is present. From 5bb783c7602aa69ee35f135a53ef25bd83da45d8 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Oct 2025 15:23:21 -0400 Subject: [PATCH 44/47] Apply suggestions from code review Co-authored-by: Jon Skeet --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 5b6186a76..d1aad031f 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -247,7 +247,7 @@ This clause augments the description of members in classes (§15.3) with restric Some declarations, such as *constant_declaration* (§15.4) have no restrictions in interfaces. -The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does cause a compiler to issue a warning. To suppress the warning, the declaration of the derived interface member shall include a `new` modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in (§7.7.2.3). +The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does result in a warning (§7.7.2.3). If a `new` modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the `new` modifier. From 20b278cb6609c2dab1df1d3e528a107169515852 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Oct 2025 15:23:49 -0400 Subject: [PATCH 45/47] Apply suggestions from code review --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index d1aad031f..248e491a8 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -249,7 +249,7 @@ Some declarations, such as *constant_declaration* (§15.4) have no restrictions The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to *hide* the base interface member. Hiding an inherited member is not considered an error, but it does result in a warning (§7.7.2.3). -If a `new` modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the `new` modifier. +If a `new` modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. > *Note*: The members in class `object` are not, strictly speaking, members of any interface ([§19.4](interfaces.md#194-interface-members)). However, the members in class `object` are available via member lookup in any interface type ([§12.5](expressions.md#125-member-lookup)). *end note* From 1d258210d9a32a8f2c0c4204718bac00508d271e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Oct 2025 15:24:18 -0400 Subject: [PATCH 46/47] Apply suggestions from code review Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/interfaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/interfaces.md b/standard/interfaces.md index 248e491a8..3c1328959 100644 --- a/standard/interfaces.md +++ b/standard/interfaces.md @@ -718,7 +718,7 @@ When an interface is part of a namespace, a qualified interface member name can Interfaces may be implemented by classes and structs. To indicate that a class or struct directly implements an interface, the interface is included in the base class list of the class or struct. -A class or struct `C` that implements an interface `I` must provide or inherit an implementation for every member declared in `I`. Public members of `I` may be defined in public members of `C`. Non-public members declared in `I` that are accessible in `C` may be defined in `C` using explicit interface implmentation (§19.6.2). Non-accessible members, such as `private` members of `I` can't be defined in `C`. +A class or struct `C` that implements an interface `I` must provide or inherit an implementation for every member declared in `I` that `C` can access. Public members of `I` may be defined in public members of `C`. Non-public members declared in `I` that are accessible in `C` may be defined in `C` using explicit interface implementation (§19.6.2). A member in a derived type that satisfies interface mapping (§19.6.5) but does not implement the matching base interface member introduces a new member. This occurs when explicit interface implementation is required to define the interface member. From aa22e860a5287e3b5c83af687f7ddf94c86d7f12 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Oct 2025 15:25:17 -0400 Subject: [PATCH 47/47] Apply suggestions from code review --- standard/basic-concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index faf0f1a5c..18d32c4e5 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -402,7 +402,7 @@ As described in [§7.4](basic-concepts.md#74-members), all members of a base cla ### 7.5.4 Protected access -When a `protected` or `private protected` instance member is accessed outside the program text of the class in which it is declared, and when a `protected internal` instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place *through* an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class. Instance interface members defined with `protected` or `private protected` access cannot be accessed from a `class` or `struct` that implements that interface; these can be accessed only from derived interfaces. However, `class` and `struct` types can override `protected` instance members declared in an interface it implements. +When a `protected` or `private protected` instance member is accessed outside the program text of the class in which it is declared, and when a `protected internal` instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place *through* an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class. Instance interface members defined with `protected` or `private protected` access cannot be accessed from a `class` or `struct` that implements that interface; these can be accessed only from derived interfaces. However, `class` and `struct` types can define overridden `protected` instance members declared in an interface it implements. Let `B` be a base class that declares a protected instance member `M`, and let `D` be a class that derives from `B`. Within the *class_body* of `D`, access to `M` can take one of the following forms: