You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A *struct_declaration* consists of an optional set of *attributes* ([§23](attributes.md#23-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional `ref` modifier ([§16.2.3](structs.md#1623-ref-modifier)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-type-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *struct_interfaces* specification ([§16.2.5](structs.md#1625-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *struct_body* ([§16.2.6](structs.md#1626-struct-body)), optionally followed by a semicolon.
41
+
A *struct_declaration* is for either a ***non-record struct*** or a ***record struct***.
42
+
43
+
A *non_record_struct_declaration* consists of an optional set of *attributes* ([§23](attributes.md#23-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional `ref` modifier ([§16.2.3](structs.md#1623-ref-modifier)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-type-declarations)), followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *struct_interfaces* specification ([§16.2.5](structs.md#1625-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *struct_body* ([§16.2.6](structs.md#1626-struct-body)), optionally followed by a semicolon.
44
+
45
+
A *record_struct_declaration* consists of an optional set of *attributes* ([§23](attributes.md#23-attributes)), followed by an optional set of *struct_modifier*s ([§16.2.2](structs.md#1622-struct-modifiers)), followed by an optional partial modifier ([§15.2.7](classes.md#1527-partial-type-declarations)), followed by the keyword `record`, followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([§15.2.3](classes.md#1523-type-parameters)), followed by an optional *delimited_parameter_list* specification ([§15.6.2.1](standard/classes.md#15621-general)), followed by an optional *struct_interfaces* specification ([§16.2.5](structs.md#1625-struct-interfaces)), followed by an optional *type_parameter_constraints-clauses* specification ([§15.2.5](classes.md#1525-type-parameter-constraints)), followed by a *record_struct_body*.
26
46
27
-
A struct declaration shall not supply *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*.
47
+
A *struct_declaration* shall not supply *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*.
28
48
29
-
A struct declaration that supplies a *type_parameter_list* is a generic struct declaration. Additionally, any struct nested inside a generic class declaration or a generic struct declaration is itself a generic struct declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)).
49
+
A *struct_declaration* that supplies a *type_parameter_list* is a generic struct declaration. Additionally, any struct nested inside a generic class declaration or a generic struct declaration is itself a generic struct declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)).
30
50
31
-
A struct declaration that includes a `ref` keyword shall not have a *struct_interfaces* part.
51
+
A *non_record_struct_declaration* that includes a `ref` modifier shall not have a *struct_interfaces* part.
52
+
53
+
A *record_struct_declaration* having a *delimited_parameter_list* declares a ***positional record struct***.
54
+
55
+
At most only one *record_struct_declaration* containing `partial` may provide a *delimited_parameter_list*.
56
+
57
+
The parameters in *delimited_parameter_list* shall not have `ref`, `out` or `this` modifiers; however, `in` and `params` modifiers are permitted.
58
+
For a *record_struct_declaration*, the *record_struct_body*s `{}`, `{};`, and `;` are equivalent. They all indicate that the only members are those synthesized by the compiler (§synth-members).
32
59
33
60
### 16.2.2 Struct modifiers
34
61
@@ -63,7 +90,7 @@ When an instance of a readonly struct is passed to a method, its `this` is treat
63
90
64
91
### 16.2.3 Ref modifier
65
92
66
-
The `ref` modifier indicates that the *struct_declaration* declares a type whose instances are allocated on the execution stack. These types are called ***ref struct*** types. The `ref` modifier declares that instances may contain ref-like fields, and shall not be copied out of its safe-context ([§16.4.15](structs.md#16415-safe-context-constraint)). The rules for determining the safe context of a ref struct are described in [§16.4.15](structs.md#16415-safe-context-constraint).
93
+
The `ref` modifier indicates that the *non_record_struct_declaration* declares a type whose instances are allocated on the execution stack. These types are called ***ref struct*** types. The `ref` modifier declares that instances may contain ref-like fields, and shall not be copied out of its safe-context ([§16.4.15](structs.md#16415-safe-context-constraint)). The rules for determining the safe context of a ref struct are described in [§16.4.15](structs.md#16415-safe-context-constraint).
67
94
68
95
It is a compile-time error if a ref struct type is used in any of the following contexts:
69
96
@@ -114,7 +141,7 @@ struct_body
114
141
115
142
## 16.3 Struct members
116
143
117
-
The members of a struct consist of the members introduced by its *struct_member_declaration*s and the members inherited from the type `System.ValueType`.
144
+
The members of a struct consist of the members introduced by its *struct_member_declaration*s and the members inherited from the type `System.ValueType`. For a record struct, the member set also includes the synthesized members generated by the compiler (§synth-members).
118
145
119
146
```ANTLR
120
147
struct_member_declaration
@@ -138,6 +165,261 @@ struct_member_declaration
138
165
139
166
Except for the differences noted in [§16.4](structs.md#164-class-and-struct-differences), the descriptions of class members provided in [§15.3](classes.md#153-class-members) through [§15.12](classes.md#1512-static-constructors) apply to struct members as well.
140
167
168
+
It is an error for a member of a record struct to be named `Clone`.
169
+
170
+
It is an error for a member of a record struct to be named `Clone`.
171
+
172
+
It is an error for an instance field of a record struct to have an unsafe type.
173
+
174
+
## §synth-members Synthesized record struct members
175
+
176
+
### §synth-members-general General
177
+
178
+
In the case of a record struct, members are synthesized unless a member with a “matching” signature is declared in the *record_struct_body* or an accessible concrete non-virtual member with a “matching” signature is inherited. Two members are considered matching if they have the same signature or would be considered “hiding” in an inheritance scenario. (See Signatures and overloading [§7.6](basic-concepts.md#76-signatures-and-overloading).)
179
+
180
+
The synthesized members are described in the following subclauses.
181
+
182
+
### §rec-struct-equalmem Equality members
183
+
184
+
The synthesized equality members are similar to those for a record class ([§15.16.2](classes.md#15162-equality-members)), except for the lack of `EqualityContract`, null checks, or inheritance.
185
+
186
+
A record struct `R` implements `System.IEquatable<R>` and includes a synthesized strongly-typed overload of `Equals(R other)`, which is public, as follows:
187
+
188
+
```csharp
189
+
publicreadonlyboolEquals(Rother);
190
+
```
191
+
192
+
This method can be declared explicitly. However, it is an error if the explicit declaration does not match the expected signature or accessibility.
193
+
194
+
If `Equals(R other)` is user-defined (that is, not synthesized) but `GetHashCode` is not, a warning shall be produced.
195
+
196
+
The synthesized `Equals(R)` shall return `true` if and only if for each instance field `fieldN` in the record struct the value of `System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)`, where `TN` is the field type, is `true`.
197
+
198
+
The record struct includes synthesized `==` and `!=` operators equivalent to operators declared as follows:
The `Equals` method called by the `==` operator is the `Equals(R other)` method specified above. The `!=` operator delegates to the `==` operator. It is an error if the operators are declared explicitly.
206
+
207
+
The record struct includes a synthesized override equivalent to a method declared as follows:
208
+
209
+
```csharp
210
+
publicoverridereadonlyboolEquals(object?obj);
211
+
```
212
+
213
+
It is an error if the override is declared explicitly. The synthesized override shall return `other is R temp && Equals(temp)` where `R` is the record struct.
214
+
215
+
The record struct includes a synthesized override equivalent to a method declared as follows:
216
+
217
+
```csharp
218
+
publicoverridereadonlyintGetHashCode();
219
+
```
220
+
221
+
This method may be declared explicitly.
222
+
223
+
A warning shall be reported if one of `Equals(R)` and `GetHashCode()` is explicitly declared but the other method is not.
224
+
225
+
The synthesized override of `GetHashCode()` shall return an `int` result of combining the values of `System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)` for each instance field `fieldN` with `TN` being the type of `fieldN`.
226
+
227
+
> *Example*: Consider the following record struct:
1. For each of the record struct’s printable members (non-static public field and readable property members), appends that member's name followed by “` = `“ followed by the member's value separated with “`, “`,
273
+
2. Returns true if the record struct has printable members.
274
+
275
+
For a member that has a value type, its value shall be converted to a string representation.
276
+
277
+
If the record’s printable members do not include a readable property with a non-`readonly``get` accessor, then the synthesized `PrintMembers` is `readonly`. There is no requirement for the record’s fields to be `readonly` for the `PrintMembers` method to be `readonly`.
278
+
279
+
The `PrintMembers` method can be declared explicitly. However, it is an error if the explicit declaration does not match the expected signature or accessibility.
280
+
281
+
The record struct includes a synthesized method equivalent to the following:
282
+
283
+
```csharp
284
+
publicoverridestringToString();
285
+
```
286
+
287
+
If the record struct’s `PrintMembers` method is `readonly`, then the synthesized `ToString()` method shall be `readonly`.
288
+
289
+
This method can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility.
290
+
291
+
This method performs the following tasks:
292
+
293
+
1. Creates a `StringBuilder` instance,
294
+
2. Appends the record struct name to the builder, followed by “` { `“,
295
+
3. Invokes the record struct's `PrintMembers` method giving it the builder, followed by “``” if it returned true,
296
+
4. Appends “`}`”,
297
+
5. Returns the builder’s contents with `builder.ToString()`.
298
+
299
+
> *Example*: Consider the following record struct:
><!--NOTE:Inreality, classR1 will also have a synthesized implementation of interface member 'IEquatable<R1>.Equals(R1?)', but as that is not relevant to this printing-member example, error CS0535 re this omission has been ignored. -->
310
+
> ```csharp
311
+
> struct R1 : IEquatable<R1>
312
+
> {
313
+
> publicT1P1 { get; set; }
314
+
> publicT2P2 { get; set; }
315
+
>
316
+
> privateboolPrintMembers(StringBuilderbuilder)
317
+
> {
318
+
>builder.Append(nameof(P1));
319
+
>builder.Append(" = ");
320
+
>builder.Append(this.P1); // or builder.Append(this.P1.ToString());
321
+
>// if P1 has a value type
322
+
>builder.Append(", ");
323
+
>
324
+
>builder.Append(nameof(P2));
325
+
>builder.Append(" = ");
326
+
>builder.Append(this.P2); // or builder.Append(this.P2.ToString());
327
+
>// if P2 has a value type
328
+
>
329
+
>returntrue;
330
+
> }
331
+
>
332
+
> publicoverridestringToString()
333
+
> {
334
+
>varbuilder=newStringBuilder();
335
+
>builder.Append(nameof(R1));
336
+
>builder.Append(" { ");
337
+
>
338
+
>if (PrintMembers(builder))
339
+
>builder.Append("");
340
+
>
341
+
>builder.Append("}");
342
+
>returnbuilder.ToString();
343
+
> }
344
+
> }
345
+
> ```
346
+
>
347
+
>*endexample*
348
+
349
+
### §rec-struct-pos-mem Positional record struct members
350
+
351
+
#### §rec-struct-pos-mem-gen General
352
+
353
+
Aswellasprovidingthemembersdescribedintheprecedingsubclauses, positionalrecordstructs ([§16.2.1](structs.md#1621-general)) synthesize additional members with the same conditions as the other members, as described in the following subclauses.
A record struct has a public constructor whose signature corresponds to the value parameters of the type declaration. This is called the primary constructor for the type. It is an error to have a primary constructor and a constructor with the same signature already present in the struct. If the type declaration does not include a *delimited_parameter_list*, no primary constructor is generated.
Instancefielddeclarationsfor a record struct are permitted to include variable initializers. If there is no primary constructor, the instance initializers execute as part of the parameterless constructor. Otherwise, at runtime the primary constructor executes the instance initializers appearing in the record-struct-body.
374
+
375
+
If a record struct has a primary constructor, any user-defined constructor shall have an explicit `this` constructor initializer that calls the primary constructor or an explicitly declared constructor.
376
+
377
+
Parameters of the primary constructor as well as members of the record struct are in scope within initializers of instance fields or properties. Instance members would be an error in these locations, but the parameters of the primary constructor would be in scope and useable and would shadow members. Static members would also be useable.
378
+
379
+
A warning shall be produced if a parameter of the primary constructor is not read.
380
+
381
+
The definite assignment rules for struct instance constructors apply to the primary constructor of record structs. For instance, the following is an error:
Foreachrecordstructparameter of a *delimited_parameter_list* there is a corresponding public property member whose name and type are taken from the value parameter declaration.
399
+
400
+
For a record struct:
401
+
402
+
- apublic `get` and `init` auto-propertyiscreatediftherecordstructhasa `readonly` modifier, `get` and `set` otherwise. Bothkindsofsetaccessors (`set` and `init`) areconsidered “matching.” So, theusermaydeclareaninit-onlypropertyinplaceofasynthesizedmutableone.
0 commit comments