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

Create Fundamentals section. #23464

Merged
merged 60 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
077956d
Update navigation
BillWagner Mar 23, 2021
a46655f
Move files
BillWagner Mar 24, 2021
28e7194
fix warnings in the fundamentals area
BillWagner Mar 24, 2021
c00b425
update ms-technology metadata.
BillWagner Mar 30, 2021
2444296
Proposed fundamentals TOC
BillWagner May 6, 2021
8d5ddfd
fix a bad merge
BillWagner May 6, 2021
b19ab3f
update see also section
BillWagner May 6, 2021
e47a5ea
One more set of TOC moves
BillWagner May 7, 2021
e5be4b5
Move program structure section
BillWagner May 7, 2021
f0105d1
clean warnings in structure section
BillWagner May 7, 2021
7dad562
warnings take 2
BillWagner May 7, 2021
a229ba2
move oo section files.
BillWagner May 7, 2021
b09e877
fix snippet build warnings.
BillWagner May 10, 2021
f4a6a37
move articles to functional section
BillWagner May 10, 2021
edad1b1
Fix a bad snippet ID conversion.
BillWagner May 11, 2021
0136fe5
Wrong snippet.
BillWagner May 11, 2021
0c41de1
Move fundamental exception content
BillWagner May 11, 2021
871f7e6
more moving exceptions
BillWagner May 11, 2021
84f4a25
move coding style articles
BillWagner May 11, 2021
8c77bcf
fix warnings.
BillWagner May 11, 2021
08140b5
build warnings
BillWagner May 11, 2021
0b6fe09
edit pass
BillWagner May 12, 2021
1e637f7
proofread.
BillWagner May 13, 2021
08aac27
continue editing types section
BillWagner May 13, 2021
20beb67
edit generics
BillWagner May 13, 2021
4a14c60
edit for anonymous types
BillWagner May 13, 2021
42497aa
fix build issues.
BillWagner May 13, 2021
343e075
edit program structure
BillWagner May 13, 2021
c743780
build error
BillWagner May 13, 2021
3b6158e
no interactive top level statements
BillWagner May 13, 2021
93d1715
update H1
BillWagner May 13, 2021
2557706
review samples in OO section
BillWagner May 14, 2021
0c863ae
finish oo tutorials
BillWagner May 14, 2021
f9a2d2a
fix issues filed on these articles
BillWagner May 14, 2021
3d422af
funtional is in place.
BillWagner May 14, 2021
70d8724
light edit on exceptions section
BillWagner May 14, 2021
3b90c34
fix warnings
BillWagner May 14, 2021
e4f9a72
finish moving coding conventions
BillWagner May 14, 2021
27b3e05
fix warnings
BillWagner May 14, 2021
f1467a3
don't use interactive on C# 9 feature
BillWagner May 14, 2021
69ead81
clean up
BillWagner May 17, 2021
02652d3
replace redirected links
BillWagner May 17, 2021
22b939c
replace relative links
BillWagner May 17, 2021
7ce5d96
fix warnings with repeated H2s
BillWagner May 17, 2021
16981ff
Apply suggestions from code review
BillWagner May 17, 2021
143331e
respond to feedback
BillWagner May 17, 2021
7010f45
fix broken links.
BillWagner May 17, 2021
c3f53fd
respond to feedback.
BillWagner May 17, 2021
b139af3
fix warnings
BillWagner May 17, 2021
3c66c9d
move fundamentals tutorials up one level.
BillWagner May 17, 2021
8a307fe
fix warnings
BillWagner May 17, 2021
f2fcfb9
Apply suggestions from code review
BillWagner May 18, 2021
3772439
Update docs/csharp/fundamentals/types/index.md
BillWagner May 18, 2021
231564d
Update docs/csharp/fundamentals/types/interfaces.md
BillWagner May 18, 2021
5387151
Apply suggestions from code review
BillWagner May 18, 2021
49b6b91
Apply suggestions from code review
BillWagner May 18, 2021
36ae584
respond to feedback.
BillWagner May 18, 2021
fc63544
missed a bad merge
BillWagner May 18, 2021
8ee0579
move remaining samples.
BillWagner May 18, 2021
cf26e6d
fix misspelling
BillWagner May 18, 2021
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
Prev Previous commit
Next Next commit
funtional is in place.
  • Loading branch information
BillWagner committed May 18, 2021
commit 3d422af88c7635d3d51ea99eb34f8fbf46bed0c8
32 changes: 16 additions & 16 deletions docs/csharp/fundamentals/functional/deconstruct.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,64 @@ ms.date: 03/22/2021
---
# Deconstructing tuples and other types

A tuple provides a lightweight 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.
A tuple provides a lightweight way to retrieve multiple values from a method call. But once you retrieve the tuple, you have to handle its individual elements. Working on an element-by-element basis is cumbersome, as the following example shows. The `QueryCityData` method returns a three-tuple, and each of its elements is assigned to a variable in a separate operation.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple1.cs":::

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.
Retrieving multiple field and property values from an object can be equally cumbersome: you must assign a field or property value to a variable on a member-by-member basis.

Starting with C# 7.0, 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.
In C# 7.0 and later, you can retrieve multiple elements from a tuple or retrieve multiple field, property, and computed values from an object in a single *deconstruct* operation. To deconstruct a tuple, you assign its elements to individual variables. When you deconstruct an object, you assign selected values to individual variables.

## Deconstructing a tuple
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

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:
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 four-tuple to four separate variables:

```csharp
var (name, address, city, zip) = contact.GetAddressInfo();
```

There are three ways to deconstruct a tuple:

- 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.
- You can explicitly declare the type of each field inside parentheses. The following example uses this approach to deconstruct the three-tuple returned by the `QueryCityData` method.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple2.cs" ID="Snippet1":::

- 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.
- 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 three-tuple returned by the `QueryCityData` method.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple3.cs" ID="Snippet1":::

You can also use the `var` keyword individually with any or all of the variable declarations inside the parentheses.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple4.cs" ID="Snippet1":::

This is cumbersome and is not recommended.
This is cumbersome and isn't recommended.

- Lastly, you may deconstruct the tuple into variables that have already been declared.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple5.cs" ID="Snippet1":::

Note that you cannot specify a specific type outside the parentheses even if every field in the tuple has the
same type. This generates compiler error CS8136, "Deconstruction 'var (...)' form disallows a specific type for 'var'.".
You can't specify a specific type outside the parentheses even if every field in the tuple has the
same type. Doing so generates compiler error CS8136, "Deconstruction 'var (...)' form disallows a specific type for 'var'.".

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."
You must assign each element of the tuple to a variable. If you omit any elements, the compiler generates error CS8132, "Can't deconstruct a tuple of 'x' elements into 'y' variables."

Note that you cannot mix declarations and assignments to existing variables on the left-hand side of a deconstruction. The compiler generates error CS8184, "a deconstruction cannot mix declarations and expressions on the left-hand-side." when the members include newly declared and existing variables.
You can't mix declarations and assignments to existing variables on the left-hand side of a deconstruction. The compiler generates error CS8184, "a deconstruction can't mix declarations and expressions on the left-hand-side." when the members include newly declared and existing variables.

## Deconstructing tuple elements with discards

Often when deconstructing a tuple, you're interested in the values of only some elements. Starting with C# 7.0, 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, `_`.
Often when deconstructing a tuple, you're interested in the values of only some elements. Starting with C# 7.0, 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 chosen by an underscore character ("\_") in an assignment. You can discard as many values as you like; all are represented by the single discard, `_`.

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.
The following example illustrates the use of tuples with discards. The `QueryCityDataForYears` method returns a six-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.

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple1.cs":::
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

## Deconstructing user-defined types

C# does not offer built-in support for deconstructing non-tuple types other than the [`record`](#deconstructing-a-record-type) and [DictionaryEntry](xref:System.Collections.DictionaryEntry.Deconstruct%2A) types. 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:
C# doesn't offer built-in support for deconstructing non-tuple types other than the [`record`](#deconstructing-a-record-type) and [DictionaryEntry](xref:System.Collections.DictionaryEntry.Deconstruct%2A) types. 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:

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple2.cs" ID="Snippet1":::

You can then deconstruct an instance of the `Person` class named `p` with an assignment like the following:
You can then deconstruct an instance of the `Person` class named `p` with an assignment like the following code:

:::code language="csharp" source="./snippets/deconstructing-tuples/deconstruct-tuple3.cs" ID="Snippet1":::

Expand All @@ -89,7 +89,7 @@ The following example deconstructs a `Person` object into four strings (the firs

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.

The following example defines two `Deconstruct` extension methods for the <xref:System.Reflection.PropertyInfo?displayProperty=nameWithType> 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 `getAccess` and `setAccess` variables.
The following example defines two `Deconstruct` extension methods for the <xref:System.Reflection.PropertyInfo?displayProperty=nameWithType> 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's 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 `getAccess` and `setAccess` variables.

[!code-csharp[Extension-deconstruct](./snippets/deconstructing-tuples/deconstruct-extension1.cs)]

Expand Down
6 changes: 3 additions & 3 deletions docs/csharp/fundamentals/functional/discards.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: Discards - C# Guide
title: Discards - unassigned discardable variables
description: Describes C#'s support for discards, which are unassigned, discardable variables, and the ways in which discards can be used.
ms.technology: csharp-fundamentals
ms.date: 09/22/2020
ms.date: 05/14/2021
---
# Discards - C# Guide
# Discards - C# Fundamentals
BillWagner marked this conversation as resolved.
Show resolved Hide resolved

Starting with C# 7.0, C# supports discards, which are placeholder variables that are intentionally unused in application code. Discards are equivalent to unassigned variables; they don't have a value. A discard communicates intent to the compiler and others that read your code: You intended to ignore the result of an expression. You may want to ignore the result of an expression, one or more members of a tuple expression, an `out` parameter to a method, or the target of a pattern matching expression.

Expand Down
11 changes: 6 additions & 5 deletions docs/csharp/fundamentals/functional/pattern-matching.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
---
title: Pattern matching overview - C# guide
description: Learn about pattern matching expressions in C#
ms.date: 04/26/2021
ms.technology: csharp-fundamentals
ms.date: 05/14/2021
---

# Pattern matching overview
Expand All @@ -25,9 +24,9 @@ The preceding example used a [*constant pattern*](../../language-reference/opera

## Type tests

Another common use for pattern matching is to test a variable to see if it matches a given type. For example, the following code tests if a variable is non-null and implements the <xref:System.IDisposable?displayProperty=nameWithType> interface. If it does, it calls the <xref:System.IDisposable.Dispose> method on that object. The declaration pattern doesn't match a `null` value, regardless of the compile-time type of the variable. The code below guards against `null`, in addition to guarding against a type that doesn't implement `IDisposable`.
Another common use for pattern matching is to test a variable to see if it matches a given type. For example, the following code tests if a variable is non-null and implements the <xref:System.Collections.Generic.IList%601?displayProperty=nameWithType> interface. If it does, it calls the <xref:System.Collections.IList.Count?displayProperty=nameWithType> method on that list. The declaration pattern doesn't match a `null` value, regardless of the compile-time type of the variable. The code below guards against `null`, in addition to guarding against a type that doesn't implement `IList`.

:::code language="csharp" source="snippets/patterns/Program.cs" ID="TypeCheckDisposable":::
:::code language="csharp" source="snippets/patterns/Program.cs" ID="MidPoint":::

The same tests can be applied in a `switch` expression to test a variable against multiple different types. You can use that information to create better algorithms based on the specific run-time type.

Expand All @@ -47,10 +46,12 @@ The preceding example shows the same algorithm, but uses string values instead o

You can use [*relational patterns*](../../language-reference/operators/patterns.md#relational-patterns) to test how a value compares to constants. For example, the following code returns the state of water based on the temperature in Fahrenheit:

:::code language="csharp" source="snippets/patterns/Simulation.cs" ID="RelationalPattern":::
:::code language="csharp" source="snippets/patterns/Simulation.cs" ID="RelationalPattern" interactive="try-dotnet":::

The preceding code also demonstrates the conjunctive `and` [*logical pattern*](../../language-reference/operators/patterns.md#logical-patterns) to check that both relational patterns match. You can also use a disjunctive `or` pattern to check that either pattern matches. The two relational patterns are surrounded by parentheses, which you can use around any pattern for clarity. The final two switch arms handle the cases for the melting point and the boiling point. Without those two arms, the compiler warns you that your logic doesn't cover every possible input.

The preceding code also demonstrates another important feature the compiler provides for pattern matching expressions: The compiler warns you if you don't handle every input value. The compiler also issues a warning if a switch arm is already handled by a previous switch arm. That gives you freedom to refactor and reorder switch expressions. Try it yourself by refactoring the preceding code in the interactive window and see when the compiler issues warnings. You'll see squiggles under the code that has warnings.

## Multiple inputs

All the patterns you've seen so far have been checking one input. You can write patterns that examine multiple properties of an object. Consider the following `Order` record:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,29 @@ public record Order(int Items, decimal Cost);
class OrderProcessor
{
// <PropertyPattern>
public double CalculateDiscount(Order order) =>
public decimal CalculateDiscount(Order order) =>
order switch
{
(Items: > 10, Cost: > 1000.00m) => 0.10,
(Items: > 5, Cost: > 500.00m) => 0.05,
Order { Cost: > 250.00m } => 0.02,
(Items: > 10, Cost: > 1000.00m) => 0.10m,
(Items: > 5, Cost: > 500.00m) => 0.05m,
Order { Cost: > 250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
var o => 0,
var order => 0m,
};
// </PropertyPattern>
}

class OrderProcessing
{
// <DeconstructPattern>
public double CalculateDiscount(Order order) =>
public decimal CalculateDiscount(Order order) =>
order switch
{
( > 10, > 1000.00m) => 0.10,
( > 5, > 50.00m) => 0.05,
Order { Cost: > 250.00m } => 0.02,
( > 10, > 1000.00m) => 0.10m,
( > 5, > 50.00m) => 0.05m,
Order { Cost: > 250.00m } => 0.02m,
null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
var o => 0,
var order => 0m,
};
// </DeconstructPattern>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace patterns
{
Expand All @@ -10,21 +11,27 @@ static void Main(string[] args)

NullReferenceCheck();

TypeCheckDisposable();
MidPointCheck();
}

private static void TypeCheckDisposable()
// <MidPoint>
public static T MidPoint<T>(IEnumerable<T> sequence)
{
object? heldReference = default;

// <TypeCheckDisposable>
if (heldReference is IDisposable disposable)
if (sequence is IList<T> list)
{
return list[list.Count / 2];
}
else if (sequence is null)
{
throw new ArgumentNullException(nameof(sequence), "Sequence can't be null.");
} else
{
disposable.Dispose();
int halfLength = sequence.Count() / 2 - 1;
if (halfLength < 0) halfLength = 0;
return sequence.Skip(halfLength).First();
}
heldReference = null;
// </TypeCheckDisposable>
}
// </MidPoint>

private static void NullReferenceCheck()
{
Expand Down
Loading