Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 72 additions & 28 deletions standard/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
: 'where' type_parameter ':' type_parameter_constraints
;

type_parameter_constraints

Check warning on line 400 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L400

MDC032::Line length 83 > maximum 81
: primary_constraint (',' secondary_constraints)? (',' constructor_constraint)?
| secondary_constraints (',' constructor_constraint)?
| constructor_constraint
Expand Down Expand Up @@ -506,15 +506,15 @@
> static void M()
> {
> // nonnull constraint allows nonnullable struct type argument
> A<int> x1;

Check warning on line 509 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L509

MDC032::Line length 87 > maximum 81
> // possible warning: nonnull constraint prohibits nullable struct type argument
> A<int?> x2;
> // nonnull constraint allows nonnullable class type argument
> A<C> x3;

Check warning on line 513 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L513

MDC032::Line length 86 > maximum 81
> // possible warning: nonnull constraint prohibits nullable class type argument
> A<C?> x4;

Check warning on line 515 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L515

MDC032::Line length 84 > maximum 81
> // nonnullable base class requirement allows nonnullable class type argument
> B1<C> x5;

Check warning on line 517 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L517

MDC032::Line length 82 > maximum 81
> // possible warning: nonnullable base class requirement prohibits nullable
> // class type argument
> B1<C?> x6;
Expand Down Expand Up @@ -2074,7 +2074,7 @@
;

ref_method_modifiers
: ref_method_modifier*
: ref_method_modifier* 'partial'?
;

method_header
Expand Down Expand Up @@ -2151,15 +2151,16 @@
- 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`.
- If the declaration includes the `partial` modifier, then it does not include the modifier `abstract`.
- If the declaration is for a restricted partial method (§restricted-partial-methods), then it does not include any of the following modifiers: `new`, `public`, `protected`, `internal`, `private`, `virtual`, `sealed`, `override`, or `extern`.

Methods are classified according to what, if anything, they return:

- If `ref` is present, the method is ***returns-by-ref*** and returns a *variable reference*, that is optionally read-only;
- Otherwise, if *return_type* is `void`, the method is ***returns-no-value*** and does not return a value;
- Otherwise, the method is ***returns-by-value*** and returns a value.

The *return_type* of a returns-by-value or returns-no-value method declaration specifies the type of the result, if any, returned by the method. Only a returns-no-value method may include the `partial` modifier ([§15.6.9](classes.md#1569-partial-methods)). If the declaration includes the `async` modifier then *return_type* shall be `void` or the method returns-by-value and the return type is a *task type* ([§15.14.1](classes.md#15141-general)).
The *return_type* of a returns-by-value or returns-no-value method declaration specifies the type of the result, if any, returned by the method. If the declaration includes the `async` modifier then *return_type* shall be `void` or the method returns-by-value and the return type is a *task type* ([§15.14.1](classes.md#15141-general)).

The *ref_return_type* of a returns-by-ref method declaration specifies the type of the variable referenced by the *variable_reference* returned by the method.

Expand Down Expand Up @@ -2388,7 +2389,7 @@

A parameter declared with an `out` modifier is an ***output parameter***. For definite-assignment rules, see [§9.2.7](variables.md#927-output-parameters).

A method declared as a partial method ([§15.6.9](classes.md#1569-partial-methods)) shall not have output parameters.
A method declared as a restricted partial method (§restricted-partial-methods) shall not have output parameters.

> *Note*: Output parameters are typically used in methods that produce multiple return values. *end note*
<!-- markdownlint-disable MD028 -->
Expand Down Expand Up @@ -3026,22 +3027,53 @@

### 15.6.9 Partial methods

When a method declaration includes a `partial` modifier, that method is said to be a ***partial method***. Partial methods may only be declared as members of partial types ([§15.2.7](classes.md#1527-partial-type-declarations)), and are subject to a number of restrictions.
#### §partial-methods-general General

Partial methods may be defined in one part of a type declaration and implemented in another. The implementation is optional; if no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts.
When a *method declaration* includes a `partial` modifier, that method is said to be a ***partial method***. Partial methods may only be declared as members of partial types ([§15.2.7](classes.md#1527-partial-type-declarations)). Partial methods may be defined in one part of a type declaration and implemented in another.

Partial methods shall not define access modifiers; they are implicitly private. Their return type shall be `void`, and their parameters shall not be output parameters. The identifier `partial` is recognized as a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)) in a method declaration only if it appears immediately before the `void` keyword. A partial method cannot explicitly implement interface methods.
In *method_declaration*, the identifier `partial` is recognized as a contextual keyword ([§6.4.4](lexical-structure.md#644-keywords)) only if it immediately precedes the *return_type*. A partial method cannot explicitly implement interface methods.

There are two kinds of partial method declarations: If the body of the method declaration is a semicolon, the declaration is said to be a ***defining partial method declaration***. If the body is other than a semicolon, the declaration is said to be an ***implementing partial method declaration***. Across the parts of a type declaration, there shall be only one defining partial method declaration with a given signature, and there shall be at most only one implementing partial method declaration with a given signature. If an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following:
There are classifications of a partial method declaration:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
There are classifications of a partial method declaration:
Partial method declarations are classified as follows:

(Or something else - I'm just not keen on the current wording.)


- A method with an *expression-body* or a *block-body* or is declared with the `extern` modifier is said to be an ***implementing partial method declaration***.
- Otherwise, a method declaration where the body of the method declaration is a semicolon is said to be a ***defining partial method declaration***.

Across the parts of a type declaration, there shall be exactly one defining partial method declaration with a given signature. For an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Across the parts of a type declaration, there shall be exactly one defining partial method declaration with a given signature. For an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following:
Across the parts of a type declaration, there shall be exactly one defining partial method declaration with a given signature. If an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following:


- The declarations shall have the same method name, number of type parameters, and number of parameters.
- The declarations shall have the same modifiers (although not necessarily in the same order), with the exception of the `async` modifier which shall not appear on a defining part.
- Corresponding parameters in the declarations shall have the same modifiers (although not necessarily in the same order) and the same types, or identity convertible types (modulo differences in type parameter names).
- The declarations shall have the same modifiers except for the `async` and `extern` modifiers. The `async` and `extern` modifiers are allowed only on the implementing partial method declaration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about readonly? I think it would make sense for an implementation to potentially be readonly even if the declaration isn't - and in that case, I'd expect the readonly-ness to be available elsewhere in the code.

- Corresponding parameters in the declarations shall have the same modifiers (although not necessarily in the same order) and the same types (modulo differences in type parameter names).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the type parameter names - do we have an example of this?

I can't remember what we say elsewhere about tuple types and "sameness" - is it valid to have:

partial void M((int x, string y) tuple);

partial void M((int a, string b) otherTuple) { ... }

?

- Corresponding type parameters in the declarations shall have the same constraints (modulo differences in type parameter names).

An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration.
There are two versions of partial methods: restricted and unrestricted. A ***restricted partial method*** (§restricted-partial-methods) has no explicit access modifiers, and is implicitly private. An ***unrestricted partial method*** (§unrestricted-partial-methods) is a partial method that includes one or more explicit access modifiers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to avoid the word "version" here. Maybe this is another "classification" (as well as defining/implementing)?


Only a defining partial method participates in overload resolution. Thus, whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method. Because a partial method always returns `void`, such invocation expressions will always be expression statements. Furthermore, because a partial method is implicitly `private`, such statements will always occur within one of the parts of the type declaration within which the partial method is declared.
For an unrestricted partial method both the definition and implementation shall exist.

> *Example*:
>
> <!-- Example: {template:"standalone-lib-without-using", name:"PartialMethods2", replaceEllipsis:true, customEllipsisReplacements: ["", "return true;", "i = 10;"]} -->
> ```csharp
> // part containing defining partial method declarations
> partial class C
> {
> partial void M1(); // restricted, impl. optional
> private partial void M2(); // unrestricted, impl. required
> protected partial bool M3(); // unrestricted, impl. required
> public partial void M4(out int i); // unrestricted, impl. required
> }
>
> // part containing implementing partial method declarations
> partial class C
> {
> private partial void M2() { ... }
> protected partial bool M3() { ... }
> public partial void M4(out int i) { ... }
> }
> ```
>
> *end example*

Only a defining partial method participates in overload resolution.

> *Note*: The definition of matching defining and implementing partial method declarations does not require parameter names to match. This can produce *surprising*, albeit *well defined*, behaviour when named arguments ([§12.6.2.1](expressions.md#12621-general)) are used. For example, given the defining partial method declaration for `M` in one file, and the implementing partial method declaration in another file:
>
Expand All @@ -3065,25 +3097,12 @@
>
> *end note*

If no part of a partial type declaration contains an implementing declaration for a given partial method, any expression statement invoking it is simply removed from the combined type declaration. Thus the invocation expression, including any subexpressions, has no effect at run-time. The partial method itself is also removed and will not be a member of the combined type declaration.

If an implementing declaration exists for a given partial method, the invocations of the partial methods are retained. The partial method gives rise to a method declaration similar to the implementing partial method declaration except for the following:

- The `partial` modifier is not included.

- All modifiers except the `partial` modifier are included in the resulting, including any `extern` or `async` modifier declared on the implementing declaration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- All modifiers except the `partial` modifier are included in the resulting, including any `extern` or `async` modifier declared on the implementing declaration.
- All modifiers except the `partial` modifier are included in the resulting method declaration, including any `extern` or `async` modifier declared on the implementing declaration.

Separately, it's not clear to me what the impact of this is.

- The attributes in the resulting method declaration are the combined attributes of the defining and the implementing partial method declaration in unspecified order. Duplicates are not removed.

- The attributes on the parameters of the resulting method declaration are the combined attributes of the corresponding parameters of the defining and the implementing partial method declaration in unspecified order. Duplicates are not removed.

If a defining declaration but not an implementing declaration is given for a partial method `M`, the following restrictions apply:

- It is a compile-time error to create a delegate from `M` ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)).

- It is a compile-time error to refer to `M` inside an anonymous function that is converted to an expression tree type ([§8.6](types.md#86-expression-tree-types)).

- Expressions occurring as part of an invocation of `M` do not affect the definite assignment state ([§9.4](variables.md#94-definite-assignment)), which can potentially lead to compile-time errors.

- `M` cannot be the entry point for an application ([§7.1](basic-concepts.md#71-application-startup)).
- Any default arguments (§15.6.2) in the implementing declaration are removed.

Partial methods are useful for allowing one part of a type declaration to customize the behavior of another part, e.g., one that is generated by a tool. Consider the following partial class declaration:

Expand Down Expand Up @@ -3167,6 +3186,31 @@
}
```

#### §restricted-partial-methods Restricted partial methods

A restricted partial method shall have a `void` return type, and shall not declare out parameters. There shall be zero or one implementing declaration for each defining declaration. If no part implements the partial method, the partial method declaration and all calls to it are removed from the type declaration resulting from the combination of the parts. Whether or not an implementing declaration is given, invocation expressions may resolve to invocations of the partial method.

The implementing member for a restricted partial method shall not be an external method (§15.6.8).

> *Note*: Because a restricted partial method always returns `void`, such invocation expressions will always be expression statements. Furthermore, because a restricted partial method is implicitly `private`, such statements will always occur within one of the parts of the type declaration within which the partial method is declared. *end note*

If a restricted partial method has no implementation, any expression statement invoking it is removed from the combined type declaration. Thus, the invocation expression, including any subexpressions, has no effect at run-time. The partial method itself is also removed and will not be a member of the combined type declaration.

If a defining declaration but not an implementing declaration is given for a restricted partial method `M`, the following restrictions apply:

- It is a compile-time error to create a delegate from `M` ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)).
- It is a compile-time error to refer to `M` inside an anonymous function that is converted to an expression tree type ([§8.6](types.md#86-expression-tree-types)).
- Expressions occurring as part of an invocation of `M` do not affect the definite assignment state ([§9.4](variables.md#94-definite-assignment)), which can potentially lead to compile-time errors.
- `M` cannot be the entry point for an application ([§7.1](basic-concepts.md#71-application-startup)).

#### §unrestricted-partial-methods Unrestricted partial methods

An unrestriced partial method declartion incluces an explicit access modifier. There shall be exactly one implementing partial method declaration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
An unrestriced partial method declartion incluces an explicit access modifier. There shall be exactly one implementing partial method declaration.
An unrestricted partial method declaration incluces an explicit access modifier. There shall be exactly one implementing partial method declaration.


The implementing declaration for an unrestricted partial method may be an external method (§15.6.8). The `extern` modifier is modifer allowed on an implementing partial declaration. It shall not be present on a defining partial declaration.

> *Note:* The `private` access modifier is required on both the ***defining partial method declaration*** and the ***implementing partial method declaration*** of a private unrestricted partial method. *end note*

### 15.6.10 Extension methods

When the first parameter of a method includes the `this` modifier, that method is said to be an ***extension method***. Extension methods shall only be declared in non-generic, non-nested static classes. The first parameter of an extension method is restricted, as follows:
Expand Down Expand Up @@ -3465,7 +3509,7 @@
> static void Main()
> {
> field = 10;
> Console.WriteLine(Property); // Prints 10

Check warning on line 3512 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L3512

MDC032::Line length 83 > maximum 81
> Property = 20; // This invokes the get accessor, then assigns
> // via the resulting variable reference
> Console.WriteLine(field); // Prints 20
Expand Down Expand Up @@ -4234,7 +4278,7 @@

The accessor declarations consist of an *add_accessor_declaration* and a *remove_accessor_declaration*. Each accessor declaration consists of the token add or remove followed by a *block*. The *block* associated with an *add_accessor_declaration* specifies the statements to execute when an event handler is added, and the *block* associated with a *remove_accessor_declaration* specifies the statements to execute when an event handler is removed.

Each *add_accessor_declaration* and *remove_accessor_declaration* corresponds to a method with a single value parameter of the event type, and a `void` return type. The implicit parameter of an event accessor is named `value`. When an event is used in an event assignment, the appropriate event accessor is used. Specifically, if the assignment operator is `+=` then the add accessor is used, and if the assignment operator is `–=` then the remove accessor is used. In either case, the right operand of the assignment operator is used as the argument to the event accessor. The block of an *add_accessor_declaration* or a *remove_accessor_declaration* shall conform to the rules for `void` methods described in [§15.6.9](classes.md#1569-partial-methods). In particular, `return` statements in such a block are not permitted to specify an expression.
Each *add_accessor_declaration* and *remove_accessor_declaration* corresponds to a method with a single value parameter of the event type, and a `void` return type. The implicit parameter of an event accessor is named `value`. When an event is used in an event assignment, the appropriate event accessor is used. Specifically, if the assignment operator is `+=` then the add accessor is used, and if the assignment operator is `–=` then the remove accessor is used. In either case, the right operand of the assignment operator is used as the argument to the event accessor. The block of an *add_accessor_declaration* or a *remove_accessor_declaration* shall conform to the rules for `void` methods described in §restricted-partial-methods. In particular, `return` statements in such a block are not permitted to specify an expression.

Since an event accessor implicitly has a parameter named `value`, it is a compile-time error for a local variable or constant declared in an event accessor to have that name.

Expand Down Expand Up @@ -4580,7 +4624,7 @@
: '!'
;

overloadable_unary_operator

Check warning on line 4627 in standard/classes.md

View workflow job for this annotation

GitHub Actions / Markdown to Word Converter

standard/classes.md#L4627

MDC032::Line length 82 > maximum 81
: '+' | '-' | logical_negation_operator | '~' | '++' | '--' | 'true' | 'false'
;

Expand Down
Loading