Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for record classes #983

Draft
wants to merge 10 commits into
base: draft-v9
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
571 changes: 556 additions & 15 deletions standard/classes.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions standard/delegates.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ delegate_declaration
;

delegate_header
: identifier '(' formal_parameter_list? ')' ';'
| identifier variant_type_parameter_list '(' formal_parameter_list? ')'
: identifier parameter_list ';'
| identifier variant_type_parameter_list parameter_list
type_parameter_constraints_clause* ';'
;

Expand Down Expand Up @@ -47,7 +47,7 @@ The delegate’s type name is *identifier*.

As with methods ([§15.6.1](classes.md#1561-general)), if `ref` is present, the delegate returns-by-ref; otherwise, if *return_type* is `void`, the delegate returns-no-value; otherwise, the delegate returns-by-value.

The optional *formal_parameter_list* specifies the parameters of the delegate.
The *parameter_list* specifies the parameters of the delegate.

The *return_type* of a returns-by-value or returns-no-value delegate declaration specifies the type of the result, if any, returned by the delegate.

Expand Down Expand Up @@ -83,7 +83,7 @@ The only way to declare a delegate type is via a *delegate_declaration*. Every d

## 20.3 Delegate members

Every delegate type inherits members from the `Delegate` class as described in [§15.3.4](classes.md#1534-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *formal_parameter_list* in the delegate declaration, whose return type matches the *return_type* or *ref_return_type* in the delegate declaration, and for returns-by-ref delegates whose *ref_kind* matches that in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§20.6](delegates.md#206-delegate-invocation)) .
Every delegate type inherits members from the `Delegate` class as described in [§15.3.4](classes.md#1534-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *parameter_list* in the delegate declaration, whose return type matches the *return_type* or *ref_return_type* in the delegate declaration, and for returns-by-ref delegates whose *ref_kind* matches that in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§20.6](delegates.md#206-delegate-invocation)).

Implementations may define additional members in the delegate type.

Expand Down
51 changes: 50 additions & 1 deletion standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3554,6 +3554,49 @@ At run-time, the expression `await t` is evaluated as follows:

An awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` should cause the delegate `r` to be invoked at most once. Otherwise, the behavior of the enclosing async function is undefined.

## §with-expressions With expressions

A *with_expression* allows for ***non-destructive mutation*** by making a new record class instance that is a copy of an existing record class instance, optionally with specified properties and fields modified.

```ANTLR
with_expression
: switch_expression
| switch_expression 'with' '{' member_initializer_list? '}'
;

```

A *with_expression* is not permitted as a statement.

The receiver type shall be non-`void` and of some record class type.

*identifier* shall be an accessible instance field or property of the receiver's type.

All non-positional properties being changed shall have both set and init accessors.

This expression is evaluated as follows:

- The receiver's clone method (§rec-class-copyclone) is invoked, and its result is converted to the receiver’s type.
- Each `member_initializer` is processed the same way as an assignment to
a field or property access of the result of the conversion. Assignments are processed in lexical order. If *member_initializer_list* is omitted, no members are changed.

> *Example*:
>
> <!-- Example: {template:"standalone-console", name:"WithExpression"} -->
> ```csharp
> Person person1 = new("Mary", "Smith") { Age = 35 }; // create an immutable record
> Person person2 = person1 with { FirstName = "Jane" }; // copy with FirstName changed
> person2 = person1 with { Age = 40 }; // copy with Age changed
> person2 = person1 with { }; // copy with no changes
>
> public record Person(string FirstName, string LastName)
> {
> public int Age { get; init; }
> }
> ```
>
> *end example*

## 12.10 Arithmetic operators

### 12.10.1 General
Expand Down Expand Up @@ -4194,7 +4237,13 @@ bool operator !=(C x, C y);

unless predefined equality operators otherwise exist for `C` (for example, when `C` is `string` or `System.Delegate`).

The operators return the result of comparing the two references for equality or non-equality. `operator ==` returns `true` if and only if `x` and `y` refer to the same instance or are both `null`, while `operator !=` returns `true` if and only if `operator ==` with the same operands would return `false`.
Both operands shall be record classes, or both shall be non-record classes.

The operators return the result of comparing the two references for equality or non-equality.

In the case of non-record class operands, `operator ==` returns `true` if and only if `x` and `y` refer to the same instance or are both `null`, while `operator !=` returns `true` if and only if `operator ==` with the same operands would return `false`.

In the case of record class operands, `operator ==` returns `true` if and only if the values of all the corresponding instance fields in the two record classes and their base record classes are equal, or both references are `null`, while `operator !=` returns `true` if and only if `operator ==` with the same operands would return `false`.

In addition to normal applicability rules ([§12.6.4.2](expressions.md#12642-applicable-function-member)), the predefined reference type equality operators require one of the following in order to be applicable:

Expand Down
6 changes: 3 additions & 3 deletions standard/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,13 @@ interface_method_declaration
;

interface_method_header
: identifier '(' formal_parameter_list? ')' ';'
| identifier type_parameter_list '(' formal_parameter_list? ')'
: identifier parameter_list ';'
| identifier type_parameter_list parameter_list
type_parameter_constraints_clause* ';'
;
```

The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *formal_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)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon.

All formal 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 formal parameter types shall also be output-safe.

Expand Down
2 changes: 1 addition & 1 deletion standard/lexical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ contextual_keyword
| 'by' | 'descending' | 'dynamic' | 'equals' | 'from'
| 'get' | 'global' | 'group' | 'into' | 'join'
| 'let' | 'nameof' | 'on' | 'orderby' | 'partial'
| 'remove' | 'select' | 'set' | 'unmanaged' | 'value'
| 'record' | 'remove' | 'select' | 'set' | 'unmanaged' | 'value'
| 'var' | 'when' | 'where' | 'yield'
;
```
Expand Down
3 changes: 3 additions & 0 deletions tools/example-templates/additional-files/T1T2T3.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class T1 { }
class T2 { }
class T3 { }
Loading