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
Update the tuples article(s) for C# 7.3 equality (#5390)
* final edit
Add add the UDT equality example
* respond to feedback
* respond to feedback.
Great comments all.
* remove digression
This suggests a tutorial that shows several idiomatic tuple scenarios.
There are two conditions where candidate field names are not projected onto the tuple field:
135
135
136
-
1. When the candidate name is a reserved tuple name. Examples include `Item3`, `ToString` or `Rest`.
136
+
1. When the candidate name is a reserved tuple name. Examples include `Item3`, `ToString`. or `Rest`.
137
137
1. When the candidate name is a duplicate of another tuple field name, either explicit or implicit.
138
138
139
139
These conditions avoid ambiguity. These names would cause an ambiguity
140
140
if they were used as the field names for a field in a tuple. Neither of these
141
-
conditions cause compiletime errors. Instead, the elements without projected names
141
+
conditions cause compile-time errors. Instead, the elements without projected names
142
142
do not have semantic names projected for them. The following examples
143
143
demonstrate these conditions:
144
144
@@ -147,11 +147,33 @@ demonstrate these conditions:
147
147
These situations do not cause compiler errors because that would be a breaking change for
148
148
code written with C# 7.0, when tuple field name projections were not available.
149
149
150
+
## Equality and tuples
151
+
152
+
Beginning with C# 7.3, tuple types support the `==` and `!=` operators. These operators work by comparing each member of the left argument to each member of the right argument in order. These comparisons short-circuit. The `==` operator stops evaluating members as soon as one pair is not equal. The `!=` operator stops evaluating members as soon as one pair is equal. The following code examples use `==`, but the comparison rules all apply to `!=`. The following code example shows an equality comparison for two pairs of integers:
153
+
154
+
[!code-csharp[TupleEquality](../../samples/snippets/csharp/tuples/tuples/program.cs#Equality"Testing tuples for equality")]
155
+
156
+
There are several rules that make tuple equality tests more convenient. Tuple equality performs [lifted conversions](/dotnet/csharp/language-reference/language-specification/conversions.md#lifted-conversion-operators) if one of the tuples is a nullable tuple, as shown in the following code:
157
+
158
+
[!code-csharp[NullableTupleEquality](../../samples/snippets/csharp/tuples/tuples/program.cs#NullableEquality"Comparing Tuples and nullable tuples")]
159
+
160
+
Tuple equality also performs implicit conversions on each member of both tuples. These include lifted conversions, widening conversions, or other implicit conversions. The following examples show that an integer 2-tuple can be compared to a long 2-tuple because of the implicit conversion from integer to long:
161
+
162
+
[!code-csharp[SnippetMemberConversions](../../samples/snippets/csharp/tuples/tuples/program.cs#SnippetMemberConversions"converting tuples for equality tests")]
163
+
164
+
The names of the tuple members do not participate in tests for equality. However, if one of the operands is a tuple literal with explicit names, the compiler generates warning CS8383 if those names do not match the names of the other operand.
165
+
In the case where both operands are tuple literals, the warning is on the right operand as shown in the following example:
166
+
167
+
[!code-csharp[MemberNames](../../samples/snippets/csharp/tuples/tuples/program.cs#SnippetMemberNames"Tuple member names do not participate in equality tests")]
168
+
169
+
Finally, tuples may contain nested tuples. Tuple equality compares the "shape" of each operand through nested tuples as shown in the following example:
170
+
171
+
[!code-csharp[NestedTuples](../../samples/snippets/csharp/tuples/tuples/program.cs#SnippetNestedTuples"Tuples may contain nested tuples that participate in tuple equality.")]
172
+
150
173
## Assignment and tuples
151
174
152
175
The language supports assignment between tuple types that have
153
-
the same number of elements and implicit conversions for the types for each of those
154
-
elements. Other
176
+
the same number of elements, where each right-hand side element can be implicitly converted to its corresponding left-hand side element. Other
155
177
conversions are not considered for assignments. Let's look at the kinds
156
178
of assignments that are allowed between tuple types.
157
179
@@ -162,7 +184,7 @@ Consider these variables used in the following examples:
162
184
The first two variables, `unnamed` and `anonymous` do not have semantic
163
185
names provided for the elements. The field names are `Item1` and `Item2`.
164
186
The last two variables, `named` and `differentName` have semantic names
165
-
given for the elements. Note that these two tuples have different names
187
+
given for the elements. These two tuples have different names
166
188
for the elements.
167
189
168
190
All four of these tuples have the same number of elements (referred to as 'cardinality')
@@ -185,7 +207,7 @@ named = differentShape;
185
207
186
208
## Tuples as method return values
187
209
188
-
One of the most common uses for Tuples is as a method return
210
+
One of the most common uses for tuples is as a method return
189
211
value. Let's walk through one example. Consider this method
190
212
that computes the standard deviation for a sequence of numbers:
191
213
@@ -199,8 +221,8 @@ that computes the standard deviation for a sequence of numbers:
199
221
> text for more details on the differences between these formulas
200
222
> for standard deviation.
201
223
202
-
This follows the textbook formula for the standard deviation. It produces
203
-
the correct answer, but it's a very inefficient implementation. This
224
+
The preceding code follows the textbook formula for the standard deviation. It produces
225
+
the correct answer, but it's an inefficient implementation. This
204
226
method enumerates the sequence twice: Once to produce the average, and
205
227
once to produce the average of the square of the difference of the average.
206
228
(Remember that LINQ queries are evaluated lazily, so the computation of
@@ -214,14 +236,11 @@ and the sum of the each value squared:
214
236
215
237
[!code-csharp[SumOfSquaresFormula](../../samples/snippets/csharp/tuples/tuples/statistics.cs#06_SumOfSquaresFormula"Compute Standard Deviation using the sum of squares")]
216
238
217
-
This version enumerates the sequence exactly once. But, it's not very
218
-
reusable code. As you keep working, you'll find that many different
239
+
This version enumerates the sequence exactly once. But it's not reusable code. As you keep working, you'll find that many different
219
240
statistical computations use the number of items in the sequence,
220
-
the sum of the sequence, and the sum
241
+
the sum of the sequence, and the sum
221
242
of the squares of the sequence. Let's refactor this method and write
222
-
a utility method that produces all three of those values.
223
-
224
-
This is where tuples come in very useful.
243
+
a utility method that produces all three of those values. All three values can be returned as a tuple.
225
244
226
245
Let's update this method so the three values computed during the enumeration
227
246
are stored in a tuple. That creates this version:
@@ -238,7 +257,7 @@ The language enables a couple more options that you can use, if you want
238
257
to make a few quick edits by hand. First, you can use the `var`
239
258
declaration to initialize the tuple result from the `ComputeSumAndSumOfSquares`
240
259
method call. You can also create three discrete variables inside the
241
-
`ComputeSumAndSumOfSquares` method. The final version is below:
260
+
`ComputeSumAndSumOfSquares` method. The final version is shown in the following code:
242
261
243
262
[!code-csharp[CleanedTupleVersion](../../samples/snippets/csharp/tuples/tuples/statistics.cs#09_CleanedTupleVersion"After final cleanup")]
type outside the parentheses, even if every field in the tuple has the
333
352
same type.
334
353
@@ -347,10 +366,10 @@ public class Point
347
366
> [!WARNING]
348
367
> You cannot mix existing declarations with declarations inside the parentheses. For instance, the following is not allowed: `(var x, y) = MyMethod();`. This produces error CS8184 because *x* is declared inside the parentheses and *y* is previously declared elsewhere.
349
368
350
-
### Deconstructing userdefined types
369
+
### Deconstructing user-defined types
351
370
352
371
Any tuple type can be deconstructed as shown above. It's also easy
353
-
to enable deconstruction on any userdefined type (classes, structs, or
372
+
to enable deconstruction on any user-defined type (classes, structs, or
354
373
even interfaces).
355
374
356
375
The type author can define one or more `Deconstruct` methods that
@@ -372,7 +391,7 @@ The `Deconstruct` method can be an extension method that unpackages
372
391
the accessible data members of an object. The example below shows
373
392
a `Student` type, derived from the `Person` type, and an extension
374
393
method that deconstructs a `Student` into three variables, representing
375
-
the `FirstName`, the `LastName` and the `GPA`:
394
+
the `FirstName`, the `LastName`, and the `GPA`:
376
395
377
396
[!code-csharp[ExtensionDeconstructMethod](../../samples/snippets/csharp/tuples/tuples/person.cs#13_ExtensionDeconstructMethod"Type with a deconstruct extension method")]
378
397
@@ -385,15 +404,25 @@ the last name are returned.
385
404
386
405
[!code-csharp[Deconstruct extension method](../../samples/snippets/csharp/tuples/tuples/program.cs#13A_DeconstructExtension"Deconstruct a class type using an extension method")]
387
406
388
-
You should be very careful defining multiple `Deconstruct` methods in a
407
+
You should be careful defining multiple `Deconstruct` methods in a
389
408
class or a class hierarchy. Multiple `Deconstruct` methods that have the
390
409
same number of `out` parameters can quickly cause ambiguities. Callers may
391
410
not be able to easily call the desired `Deconstruct` method.
392
411
393
-
In this example, there is minimal chance for an ambiguous call because the
412
+
In this example, there is minimal chance for an ambiguous call because the
394
413
`Deconstruct` method for `Person` has two output parameters, and the `Deconstruct`
395
414
method for `Student` has three.
396
415
416
+
Deconstruction operators do not participate in testing equality. The following example generates compiler error CS0019:
417
+
418
+
```csharp
419
+
Personp=newPerson("Althea", "Goodwin");
420
+
if (("Althea", "Goodwin") ==p)
421
+
Console.WriteLine(p);
422
+
```
423
+
424
+
The `Deconstruct` method could convert the `Person` object `p` to a tuple containing two strings, but it is not applicable in the context of equality tests.
425
+
397
426
## Conclusion
398
427
399
428
The new language and library support for named tuples makes it much easier
@@ -402,6 +431,6 @@ but do not define behavior, as classes and structs do. It's
402
431
easy and concise to use tuples for those types. You get all the benefits of
403
432
static type checking, without needing to author types using the more
404
433
verbose `class` or `struct` syntax. Even so, they are most useful for utility methods
405
-
that are `private`, or `internal`. Create userdefined types, either
434
+
that are `private`, or `internal`. Create user-defined types, either
406
435
`class` or `struct` types when your public methods return a value
0 commit comments