Skip to content

Commit 9fc16c6

Browse files
author
Ron Petrusha
authored
C# deconstruct and discards topics (dotnet#2737)
* First commit for deconstruction/discards * Wrote more * Interim commit * Finished writing discards topic * Updated TOC * Updated what's new topic * Incorporated feedback * Corrected bad code link * Fixed broekn link
1 parent c69e1a3 commit 9fc16c6

19 files changed

+788
-2
lines changed

docs/csharp/deconstruct.md

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
title: Deconstructing tuples and other types | Microsoft Docs
3+
description: Learn how to deconstruct tuples and other types
4+
keywords: .NET, .NET Core, C#0
5+
author: rpetrusha
6+
ms-author: ronpet
7+
ms.date: 07/18/2016
8+
ms.topic: article
9+
ms.prod: .net
10+
ms.technology: devlang-csharp
11+
ms.devlang: csharp
12+
ms.assetid: 0b0c4b0f-4a47-4f66-9b8e-f5c63b195960
13+
---
14+
15+
# Deconstructing tuples and other types #
16+
17+
A tuple provides a light-weight way to retrieve multiple values from a method call. But once you retrieve the tuple, you have to handle its individual elements. Doing this on an element-by-element basis is cumbersome, as the following example shows. The `QueryCityData` method returns a 3-tuple, and each of its elements is assigned to a variable in a separate operation.
18+
19+
[!code-csharp[WithoutDeconstruction](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-tuple1.cs)]
20+
21+
Retrieving multiple field and property values from an object can be equally cumbersome: you have to assign a field or property value to a variable on a member-by-member basis.
22+
23+
Starting with C# 7, you can retrieve multiple elements from a tuple or retrieve multiple field, property, and computed values from an object in a single *deconstruct* operation. When you deconstruct a tuple, you assign its elements to individual variables. When you deconstruct an object, you assign selected values to individual variables.
24+
25+
## Deconstructing a tuple
26+
27+
C# features built-in support for deconstructing tuples, which lets you unpackage all the items in a tuple in a single operation. The general syntax for deconstructing a tuple is similar to the syntax for defining one: you enclose the variables to which each element is to be assigned in parentheses in the left side of an assignment statement. For example, the following statement assigns the elements of a 4-tuple to four separate variables:
28+
29+
```csharp
30+
var (name, address, city, zip) = contact.GetAddressInfo();
31+
```
32+
33+
There are two ways to deconstruct a tuple:
34+
35+
- You can explicitly declare the type of each field inside parentheses. The following example uses this approach to deconstruct the 3-tuple returned by the `QueryCityData` method.
36+
37+
[!code-csharp[Deconstruction-Explicit](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-tuple2.cs#1)]
38+
39+
- You can use the `var` keyword so that C# infers the type of each variable. You place the `var` keyword outside of the parentheses. The following example uses type inference when deconstructing the 3-tuple returned by the `QueryCityData` method.
40+
41+
[!code-csharp[Deconstruction-Infer](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-tuple3.cs#1)]
42+
43+
You can also use the `var` keyword individually with any or all of the variable declarations inside the parentheses.
44+
45+
[!code-csharp[Deconstruction-Infer-Some](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-tuple4.cs#1)]
46+
47+
This is cumbersome and is not recommended.
48+
49+
Note that you cannot specify a specific type outside the parentheses even if every field in the tuple has the
50+
same type. This generates compiler error CS8136, "`var (...)` form disallows a specific type for `var`.
51+
52+
Note that you must also assign each element of the tuple to a variable. If you omit any elements, the compiler generates error CS8132, "Cannot deconstruct a tuple of 'x' elements into 'y' variables."
53+
54+
## Deconstructing tuple elements with discards
55+
56+
Often when deconstructing a tuple, you're interested in the values of only some elements. Starting with C# 7, you can take advantage of C#'s support for *discards*, which are write-only variables whose values you've chosen to ignore. A discard is designated by an underscore character ("_") in an assignment. You can discard as many values as you like; all are represented by the single discard, `_`.
57+
58+
The following example illustrates the use of tuples with discards. The `QueryCityDataForYears` method returns a 6-tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. The example shows the change in population between those two years. Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards.
59+
60+
[!code-csharp[Tuple-discard](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)]
61+
62+
### Deconstructing user-defined types
63+
64+
Non-tuple types do not offer built-in support for discards. However, as the author of a class, a struct, or an interface, you can allow instances of the type to be deconstructed by implementing one or more `Deconstruct` methods. The method returns void, and each value to be deconstructed is indicated by an [out](language-reference/keywords/out-parameter-modifier.md) parameter in the method signature. For example, the following `Deconstruct` method of a `Person` class returns the first, middle, and last name:
65+
66+
[!code-csharp[Class-deconstruct](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-class1.cs#1)]
67+
68+
You can then deconstruct an instance of the `Person` class named `p` with an assignment like the following:
69+
70+
[!code-csharp[Class-deconstruct](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-class1.cs#2)]
71+
72+
The following example overloads the `Deconstruct` method to return various combinations of properties of a `Person` object. Individual overloads return:
73+
74+
- A first and last name.
75+
- A first, last, and middle name.
76+
- A first name, a last name, a city name, and a state name.
77+
78+
[!code-csharp[Class-deconstruct](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-class2.cs)]
79+
80+
Because you can overload the `Deconstruct` method to reflect groups of data that are commonly extracted from an object, you should be careful to define `Deconstruct` methods with signatures that are distinctive and unambiguous. Multiple `Deconstruct` methods that have the same number of `out` parameters or the same number and type of `out` parameters in a different order can cause confusion.
81+
82+
The overloaded `Deconstruct` method in the following example illustrates one possible source of confusion. The first overload returns the first name, middle name, last name, and age of a `Person` object, in that order. The second overload returns name information only along with annual income, but the first, middle, and last name are in a different order. This makes it easy to confuse the order of arguments when deconstructing a `Person` instance.
83+
84+
[!code-csharp[Deconstruct-ambiguity](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-ambiguous.cs)]
85+
86+
## Deconstructing a user-defined type with discards
87+
88+
Just as you do with [tuples](#deconstructing-tuple-elements-with-discards), you can use discards to ignore selected items returned by a `Deconstruct` method. Each discard is defined by a variable named "_", and a single deconstruction operation can include multiple discards.
89+
90+
The following example deconstructs a `Person` object into four strings (the first and last names, the city, and the state) but discards the last name and the state.
91+
92+
[!code-csharp[Class-discard](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/class-discard1.cs#1)]
93+
94+
## Deconstructing a user-defined type with an extension method
95+
96+
If you didn't author a class, struct, or interface, you can still deconstruct objects of that type by implementing one or more `Deconstruct` [extension methods](programming-guide/classes-and-structs/extension-methods.md) to return the values in which you're interested.
97+
98+
The following example defines two `Deconstruct` extension methods for the <xref:System.Reflection.PropertyInfo?displayProperty=fullName> class. The first returns a set of values that indicate the characteristics of the property, including its type, whether it's static or instance, whether it's read-only, and whether it's indexed. The second indicates the property's accessibility. Because the accessibility of get and set accessors can differ, Boolean values indicate whether the property has separate get and set accessors and, if it does, whether they have the same accessibility. If there is only one accessor or both the get and the set accessor have the same accessibility, the `access` variable indicates the accessibility of the property as a whole. Otherwise, the accessibility of the get and set accessors are indicated by the accessaccessibility is indicated by the `getAccess` and `setAccess` variables.
99+
100+
[!code-csharp[Extension-deconstruct](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/deconstruct-extension1.cs)]
101+
102+
## See also
103+
[Discards](discards.md)
104+
[Tuples](tuples.md)

docs/csharp/discards.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
title: Discards - C# Guide| Microsoft Docs
3+
description: Describes C#'s support for discards, which are unassigned, discardable variables, and the ways in which discards can be used.
4+
keywords: .NET,.NET Core
5+
author: rpetrusha
6+
ms.author: ronpet
7+
ms.date: 07/21/2017
8+
ms.topic: article
9+
ms.prod: .net
10+
ms.technology: devlang-csharp
11+
ms.devlang: csharp
12+
---
13+
# Discards - C# Guide
14+
15+
Starting with C# 7, C# supports discards, which are temporary, dummy variables that are intentionally unused in application code. Discards are equivalent to unassigned variables; they do not have a value. Because there is only a single discard variable, and that variable may not even be allocated storage, discards can reduce memory allocations. Because they make the intent of your code clear, they enhance its readability and maintainability.
16+
17+
You indicate that a variable is a discard by assigning it the underscore (`_`) as its name. For example, the following method call returns a 3-tuple in which the first and second values are discards:
18+
19+
```csharp
20+
(var _, _, area) = city.GetCityInformation(cityName);
21+
```
22+
23+
In C# 7, discards are supported in assignments in the following contexts:
24+
25+
- Tuple and object [deconstruction](deconstruct.md).
26+
- Pattern matching with [is](language-reference/keywords/is.md) and [switch](language-reference/keywords/switch.md).
27+
- Calls to methods with `out` parameters.
28+
- A standalone `_` when no `_` is in scope.
29+
30+
When `_` is a valid discard, attempting to retrieve its value or use it in an assignment operation generates compiler error CS0301, "The name '_' does not exist in the current context". This is because `_` is not assigned a value, and may not even be assigned a storage location. If it were an actual variable, you could not discard more than one value, as the previous example did.
31+
32+
## Tuple and object deconstruction
33+
34+
Discards are particularly useful in working with tuples when your application code uses some tuple elements but ignores others. For example, the following `QueryCityDataForYears` method returns a 6-tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. The example shows the change in population between those two years. Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards.
35+
36+
[!code-csharp[Tuple-discard](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)]
37+
38+
For more information on deconstructing tuples with discards, see [Deconstructing tuples and other types](deconstruct.md#deconstructing-tuple-elements-with-discards).
39+
40+
The `Deconstruct` method of a class, structure, or interface also allows you to retrieve and deconstruct a specific set of data from an object. You can use discards when you are interested in working with only a subset of the deconstructed values. Ihe following example deconstructs a `Person` object into four strings (the first and last names, the city, and the state), but discards the last name and the state.
41+
42+
[!code-csharp[Class-discard](../../samples/snippets/csharp/programming-guide/deconstructing-tuples/class-discard1.cs)]
43+
44+
For more information on deconstructing user-defined types with discards, see [Deconstructing tuples and other types](deconstruct.md#deconstructing-a-user-defined type-with-discards).
45+
46+
## Pattern matching with `switch` and `is`
47+
48+
The *discard pattern* can be used in pattern matching with the [is](language-reference/keywords/is.md) and [switch](language-reference/keywords/switch.md) keywords. Every expression always matches the discard pattern.
49+
50+
The following example defines a `ProvidesFormatInfo` method that uses [is](language-reference/keywords/is.md) statements to determine whether an object provides an <xref:System.IFormatProvider> implementation and tests whether the object is `null`. It also uses the discard pattern to handle non-null objects of any other type.
51+
52+
[!code-csharp[discard-pattern](../../samples/snippets/csharp/programming-guide/discards/discard-pattern2.cs)]
53+
54+
## Calls to methods with out parameters
55+
56+
When calling the `Deconstruct` method to deconstruct a user-defined type (an instance of a class, structure, or interface), you can discard the values of individual `out` arguments. But you can also discard the value of `out` arguments when calling any method with an out parameter.
57+
58+
The following example calls the [DateTime.TryParse(String, out DateTime)](<xref:System.DateTime.TryParse(System.String,System.DateTime@)>) method to determine whether the string representation of a date is valid in the current culture. Because the example is concerned only with validating the date string and not with parsing it to extract the date, the `out` argument to the method is a discard.
59+
60+
[!code-csharp[discard-with-out](../../samples/snippets/csharp/programming-guide/discards/discard-out1.cs)]
61+
62+
## A standalone discard
63+
64+
You can use a standalone discard to indicate any variable that you choose to ignore. The following example uses a standalone discard to ignore the <xref:System.Threading.Tasks.Task> object returned by an asynchronous operation. This has the effect of suppressing the exception that the operation throws as it is about to complete.
65+
66+
[!code-csharp[standalone-discard](../../samples/snippets/csharp/programming-guide/discards/standalone-discard1.cs)]
67+
68+
Note that `_` is also a valid identifier. When used outside of a supported context, `_` is treated not as a discard but as a valid variable. If an identifier named `_` is already in scope, the use of `_` as a standalone discard can result in:
69+
70+
- Accidental modification of the value of the in-scope `_` variable by assigning it the value of the intended discard. For example:
71+
72+
[!code-csharp[standalone-discard](../../samples/snippets/csharp/programming-guide/discards/standalone-discard2.cs#1)]
73+
74+
- A compiler error for violating type safety. For example:
75+
76+
[!code-csharp[standalone-discard](../../samples/snippets/csharp/programming-guide/discards/standalone-discard2.cs#2)]
77+
78+
- Compiler error CS0136, "A local or parameter named '_' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter." For example:
79+
80+
[!code-csharp[standalone-discard](../../samples/snippets/csharp/programming-guide/discards/standalone-discard2.cs#3)]
81+
82+
## See also
83+
[Deconstructing tuples and other types](deconstruct.md)
84+
[`is` keyword](language-reference/keywords/is.md)
85+
[`switch` keyword](language-reference/keywords/switch.md)

docs/csharp/misc/sorry-we-don-t-have-specifics-on-this-csharp-error.md

+2
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,8 @@ f1_keywords:
875875
- "CS0589"
876876
- "CS0656"
877877
- "CS8038"
878+
- "CS8132"
879+
- "CS8136"
878880
- "CS8137"
879881
- "CS8139"
880882
- "CS8156"

docs/csharp/whats-new/csharp-7.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ C# 7 adds a number of new features to the C# language:
1919
- You can declare `out` values inline as arguments to the method where they are used.
2020
* [Tuples](#tuples)
2121
- You can create lightweight, unnamed types that contain multiple public fields. Compilers and IDE tools understand the semantics of these types.
22+
* [Discards](#discards)
23+
Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. They are particularly useful when deconstructing tuples and user-defined types, as well as when calling methods with `out` parameters.
2224
* [Pattern Matching](#pattern-matching)
2325
- You can create branching logic based on arbitrary types and values of the members of those types.
2426
* [`ref` locals and returns](#ref-locals-and-returns)
@@ -198,6 +200,26 @@ can rename the extract variables as part of the assignment:
198200
You can learn more in depth about tuples in the
199201
[tuples topic](../tuples.md).
200202

203+
## Discards
204+
205+
Often when deconstructing a tuple or calling a method with `out` parameters, you're forced to define a variable whose value you don't care about and don't intend to use. C# adds support for *discards* to handle this scenario. A discard is a write-only variable whose name is `_` (the underscore character); you can assign all of the values that you intend to discard to the single variable. A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.
206+
207+
Discards are supported in the following scenarios:
208+
209+
* When deconstructing tuples or user-defined types.
210+
211+
* When calling methods with [out](..\language-reference\keywords\out.md) parameters.
212+
213+
* In a pattern matching operation with the [is](..\language-reference\keywords\is.md) and [switch](language-reference\keywords\switch.md) statements.
214+
215+
* As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.
216+
217+
The following example defines a `QueryCityDataForYears` method that returns a 6-tuple that contains a data for a city for two different years. The method call in the example is concerned only with the two population values returned by the method and so treats the remaining values in the tuple as discards when it deconstructs the tuple.
218+
219+
[!code-csharp[Tuple-discard](../../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)]
220+
221+
For more information, see [Discards](../discards.md).
222+
201223
## Pattern matching
202224

203225
*Pattern matching* is a feature that allows you to implement method dispatch on
@@ -535,7 +557,7 @@ feature:
535557

536558
> [!NOTE]
537559
> You need to add the NuGet package [`System.Threading.Tasks.Extensions`](https://www.nuget.org/packages/System.Threading.Tasks.Extensions/)
538-
> in order to use the <xref:System.Threading.Tasks.Task.ValueTask%601> type.
560+
> in order to use the <xref:System.Threading.Tasks.ValueTask%601> type.
539561
540562
A simple optimization would be to use `ValueTask` in places where
541563
`Task` would be used before. However, if you want to perform extra

docs/toc.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,14 @@
191191
### [Classes](csharp/classes.md)
192192
### [Structs](csharp/structs.md)
193193
### [Tuples](csharp/tuples.md)
194+
### [Deconstructing tuples and other types](deconstruct.md)
194195
### [Interfaces](csharp/programming-guide/interfaces/index.md)
195196
<!--### [🔧 Methods and Lambda Expressions](csharp/methods-lambda-expressions.md)-->
196-
#### [Methods](csharp/methods.md)
197+
### [Methods](csharp/methods.md)
197198
#### [Lambda Expressions](csharp/lambda-expressions.md)
198199
### [Properties](csharp/properties.md)
199200
### [Indexers](csharp/indexers.md)
201+
### [Discards](discards.md)
200202
### [Generics](csharp/programming-guide/generics/index.md)
201203
### [Iterators](csharp/iterators.md)
202204
### [Delegates & events](csharp/delegates-events.md)

0 commit comments

Comments
 (0)