From 5ae6ce5110494e2577d5093686aa7e61924b41a2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Mar 2019 15:02:08 -0400 Subject: [PATCH 01/18] finish trimming "what's new" --- docs/csharp/whats-new/csharp-6.md | 2 +- docs/csharp/whats-new/csharp-7.md | 464 +++++++----------------------- docs/toc.yml | 2 + 3 files changed, 106 insertions(+), 362 deletions(-) diff --git a/docs/csharp/whats-new/csharp-6.md b/docs/csharp/whats-new/csharp-6.md index d9a68201aa4ac..99e30fdf007c3 100644 --- a/docs/csharp/whats-new/csharp-6.md +++ b/docs/csharp/whats-new/csharp-6.md @@ -8,7 +8,7 @@ ms.date: 12/12/2018 The 6.0 release of C# contained many features that improve productivity for developers. The overall effect of these features is that you write more concise code that is also more readable. The syntax contains less ceremony for many common practices. It's easier to see the design intent with less ceremony. Learn these features well, and you'll be more productive and write more readable code. You can concentrate more on your features than on the constructs of the language. -The rest of this article provides an overview of each of these features, with a link to explore each feature. You can also explore the features in an [interactive tutorial on C# 6](../tutorials/exploration/csharp-6.yml) in the tutorials section. +The rest of this article provides an overview of each of these features, with a link to explore each feature. You can also explore the features in an [interactive exploration on C# 6](../tutorials/exploration/csharp-6.yml) in the tutorials section. ## Read-only auto-properties diff --git a/docs/csharp/whats-new/csharp-7.md b/docs/csharp/whats-new/csharp-7.md index ca0e5e7ad0672..d4852df0b6554 100644 --- a/docs/csharp/whats-new/csharp-7.md +++ b/docs/csharp/whats-new/csharp-7.md @@ -1,7 +1,7 @@ --- title: What's New in C# 7.0 - C# Guide description: Get an overview of the new features in version 7.0 of the C# language. -ms.date: 12/21/2016 +ms.date: 02/20/2019 ms.assetid: fd41596d-d0c2-4816-b94d-c4d00a5d0243 --- @@ -9,11 +9,11 @@ ms.assetid: fd41596d-d0c2-4816-b94d-c4d00a5d0243 C# 7.0 adds a number of new features to the C# language: * [`out` variables](#out-variables) - - You can declare `out` values inline as arguments to the method where they are used. + - You can declare `out` values inline as arguments to the method where they're used. * [Tuples](#tuples) - You can create lightweight, unnamed types that contain multiple public fields. Compilers and IDE tools understand the semantics of these types. * [Discards](#discards) - - 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. + - Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with `out` parameters. * [Pattern Matching](#pattern-matching) - You can create branching logic based on arbitrary types and values of the members of those types. * [`ref` locals and returns](#ref-locals-and-returns) @@ -23,83 +23,43 @@ C# 7.0 adds a number of new features to the C# language: * [More expression-bodied members](#more-expression-bodied-members) - The list of members that can be authored using expressions has grown. * [`throw` Expressions](#throw-expressions) - - You can throw exceptions in code constructs that previously were not allowed because `throw` was a statement. + - You can throw exceptions in code constructs that previously weren't allowed because `throw` was a statement. * [Generalized async return types](#generalized-async-return-types) - Methods declared with the `async` modifier can return other types in addition to `Task` and `Task`. * [Numeric literal syntax improvements](#numeric-literal-syntax-improvements) - New tokens improve readability for numeric constants. -The remainder of this topic discusses each of the features. For each feature, -you'll learn the reasoning behind it. You'll learn the syntax. You'll see -some sample scenarios where using the new feature will make you more -productive as a developer. +The remainder of this article provides an overview of each feature. For each feature, +you'll learn the reasoning behind it. You'll learn the syntax. You can explore these features in our [interactive exploration](../tutorials/exploration/csharp-7.yml) of these features. ## `out` variables The existing syntax that supports `out` parameters has been improved -in this version. - -Previously, you would need to separate the declaration of the out variable -and its initialization into two different statements: - -[!code-csharp[OutVariableOldStyle](../../../samples/snippets/csharp/new-in-7/program.cs#03_OutVariableOldStyle "classic out variable declaration")] - -You can now declare `out` variables in the argument list of a method call, +in this version. You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement: -[!code-csharp[OutVariableDeclarations](../../../samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] +[!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] You may want to specify the type of the `out` variable for clarity, as shown above. However, the language does support using an implicitly typed local variable: -[!code-csharp[OutVarVariableDeclarations](../../../samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] +[!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] * The code is easier to read. - You declare the out variable where you use it, not on another line above. * No need to assign an initial value. - - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. - -The most common use for this feature will be the `Try` pattern. In this -pattern, a method returns a `bool` indicating success or failure and an -`out` variable that provides the result if the method succeeds. - -When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards: - -```csharp -if (!int.TryParse(input, out int result)) -{ - return null; -} - -return result; -``` + - By declaring the `out` variable where it's used in a method call, you can't accidentally use it before it is assigned. ## Tuples -> [!NOTE] -> The new tuples features require the types. -> You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it -> on platforms that do not include the types. -> -> This is similar to other language features that rely on types -> delivered in the framework. Example include `async` and `await` -> relying on the `INotifyCompletion` interface, and LINQ relying -> on `IEnumerable`. However, the delivery mechanism is changing -> as .NET is becoming more platform independent. The .NET Framework -> may not always ship on the same cadence as the language compiler. When new language -> features rely on new types, those types will be available as NuGet packages when -> the language features ship. As these new types get added to the .NET Standard -> API and delivered as part of the framework, the NuGet package requirement will -> be removed. - C# provides a rich syntax for classes and structs that is used to explain your design intent. But sometimes that rich syntax requires extra work with minimal benefit. You may often write methods that need a simple structure containing more than one data element. To support these scenarios *tuples* were added to C#. Tuples are lightweight data structures that contain multiple fields to represent the data members. -The fields are not validated, and you cannot define your own methods +The fields aren't validated, and you cannot define your own methods > [!NOTE] > Tuples were available before C# 7.0, @@ -109,19 +69,13 @@ The fields are not validated, and you cannot define your own methods > which enables semantic names for the fields of a tuple using new, > more efficient tuple types. -You can create a tuple by assigning a value to each member: - -[!code-csharp[UnnamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] - -That assignment creates a tuple whose members are `Item1` and `Item2`, -which you can use in the same way as -You can change the syntax to create a tuple that provides semantic +You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple: [!code-csharp[NamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and -`Beta`. Those names exist only at compile time and are not preserved +`Beta`. Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at runtime. In a tuple assignment, you can also specify the names of the fields @@ -129,48 +83,13 @@ on the right-hand side of the assignment: [!code-csharp[ImplicitNamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] -You can specify names for the fields on both the -left and right-hand side of the assignment: - -[!code-csharp[NamedTupleConflict](../../../samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] - -The line above generates a warning, `CS8123`, telling you that the names on the right -side of the assignment, `Alpha` and `Beta` are ignored because they conflict -with the names on the left side, `First` and `Second`. - -The examples above show the basic syntax to declare tuples. Tuples are -most useful as return types for `private` and `internal` methods. Tuples -provide a simple syntax for those methods to return multiple discrete values: -You save the work of authoring a `class` or a `struct` that -defines the type returned. There is no need for creating a new type. - -Creating a tuple is more efficient and more productive. -It is a simpler, lightweight syntax to define a data structure that carries -more than one value. The example method below returns the minimum and maximum -values found in a sequence of integers: - -[!code-csharp[TupleReturningMethod](../../../samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] - -Using tuples in this way offers several advantages: - -* You save the work of authoring a `class` or a `struct` that defines the type returned. -* You do not need to create new type. -* The language enhancements removes the need to call the methods. - -The declaration for the method provides the names for the fields of the -tuple that is returned. When you call the method, the return value is a -tuple whose fields are `Max` and `Min`: - -[!code-csharp[CallingTupleMethod](../../../samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] - There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables -for each of the values in the tuple. This is called *deconstructing* the tuple: +for each of the values in the tuple. This unpackaging is called *deconstructing* the tuple: [!code-csharp[CallingWithDeconstructor](../../../samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] -You can also provide a similar deconstruction for any type in .NET. This is -done by writing a `Deconstruct` method as a member of the class. That +You can also provide a similar deconstruction for any type in .NET. You write a `Deconstruct` method as a member of the class. That `Deconstruct` method provides a set of `out` arguments for each of the properties you want to extract. Consider this `Point` class that provides a deconstructor method that extracts @@ -182,13 +101,8 @@ You can extract the individual fields by assigning a `Point` to a tuple: [!code-csharp[DeconstructPoint](../../../samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] -You are not bound by the names defined in the `Deconstruct` method. You -can rename the extract variables as part of the assignment: - -[!code-csharp[DeconstructNames](../../../samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] - You can learn more in depth about tuples in the -[tuples topic](../tuples.md). +[tuples article](../tuples.md). ## Discards @@ -197,11 +111,8 @@ Often when deconstructing a tuple or calling a method with `out` parameters, you Discards are supported in the following scenarios: * When deconstructing tuples or user-defined types. - * When calling methods with [out](../language-reference/keywords/out-parameter-modifier.md) parameters. - * In a pattern matching operation with the [is](../language-reference/keywords/is.md) and [switch](../language-reference/keywords/switch.md) statements. - * As a standalone identifier when you want to explicitly identify the value of an assignment as a discard. 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. @@ -214,100 +125,73 @@ For more information, see [Discards](../discards.md). *Pattern matching* is a feature that allows you to implement method dispatch on properties other than the type of an object. You're probably already familiar -with method dispatch based on the type of an object. In Object Oriented programming, +with method dispatch based on the type of an object. In Object-Oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Base and Derived classes provide different implementations. Pattern matching expressions extend this concept so that you can easily -implement similar dispatch patterns for types and data elements that are -not related through an inheritance hierarchy. +implement similar dispatch patterns for types and data elements that aren't +related through an inheritance hierarchy. Pattern matching supports `is` expressions and `switch` expressions. Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. You use the `when` keyword to specify additional rules to the pattern. -### `is` expression - -The `is` pattern expression extends the familiar [`is` operator](../language-reference/keywords/is.md#pattern-matching-with-is) to query an object beyond its type. - -Let's start with a simple scenario. We'll add capabilities to this scenario -that demonstrate how pattern matching expressions make algorithms that work -with unrelated types easy. We'll start with a method that computes the sum -of a number of die rolls: - -[!code-csharp[SumDieRolls](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#14_SumDieRolls "Sum die rolls")] - -You might quickly find that you need to find the sum of die rolls where -some of the rolls are made with multiple dice (dice is the plural of die). Part of the input -sequence may be multiple results instead of a single number: - -[!code-csharp[SumDieRollsWithGroups](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#15_SumDieRollsWithGroups "Sum die rolls with groups")] - -The `is` pattern expression works quite well in this scenario. As part of -checking the type, you write a variable initialization. This creates -a new variable of the validated runtime type. - -As you keep extending these scenarios, you may find that you build more -`if` and `else if` statements. Once that becomes unwieldy, you'll likely -want to switch to `switch` pattern expressions. +The `is` pattern expression extends the familiar [`is` operator](../language-reference/keywords/is.md#pattern-matching-with-is) to query an object about its type and assign the result in one instruction. The following code checks if a variable is an `int`, and if so, adds it to the current sum: -### `switch` statement updates - -The *match expression* has a familiar syntax, based on the `switch` -statement already part of the C# language. Let's translate the existing code -to use a match expression before adding new cases: - -[!code-csharp[SumUsingSwitch](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#16_SumUsingSwitch "Sum using switch")] - -The match expressions have a slightly different syntax than the `is` expressions, where -you declare the type and variable at the beginning of the `case` expression. - -The match expressions also support constants. This can save time by -factoring out simple cases: - -[!code-csharp[SwitchWithConstants](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#17_SwitchWithConstants "Switch with constants")] - -The code above adds cases for `0` as a special case of `int`, and `null` -as a special case when there is no input. This demonstrates one important -new feature in switch pattern expressions: the order of the `case` -expressions now matters. The `0` case must appear before the general `int` -case. Otherwise, the first pattern to match would be the `int` case, -even when the value is `0`. If you accidentally order match expressions such -that a later case has already been handled, the compiler will flag that -and generate an error. - -This same behavior enables the special case for an empty input sequence. -You can see that the case for an `IEnumerable` item that has elements -must appear before the general `IEnumerable` case. - -This version has also added a `default` case. The `default` case is always -evaluated last, regardless of the order it appears in the source. For that -reason, convention is to put the `default` case last. +```csharp +if (input is int count) + sum += count; +``` -Finally, let's add one last `case` for a new style of die. Some games -use percentile dice to represent larger ranges of numbers. +The preceding small example demonstrates the enhancements to the `is` expression. You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type. -> [!NOTE] -> Two 10-sided percentile dice can represent every number from 0 -> through 99. One die has sides labelled `00`, `10`, `20`, ... `90`. The other -> die has sides labeled `0`, `1`, `2`, ... `9`. Add the two die values -> together and you can get every number from 0 through 99. +The switch match expression has a familiar syntax, based on the `switch` +statement already part of the C# language. The updated switch statement has several new constructs: -To add this kind of die to your collection, first define a type to represent -the percentile dice. The `TensDigit` property stores values `0`, `10`, `20`, up to `90`: +- The governing type of a `switch` expression is no longer restricted to integral types, `Enum` types, `string`, or a nullable type corresponding to one of those types. Any type may be used. +- You can test the type of the `switch` expression in each `case` label. As with the `is` expression, you may assign a new variable to that type. +- You may add a `when` clause to further test conditions on that variable. +- The order of `case` labels is now important. The first branch to match is executed; others are skipped. -[!code-csharp[18_PercentileDice](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#18_PercentileDice "Percentile Die type")] +The following code demonstrates these new features: -Then, add a `case` match expression for the new type: - -[!code-csharp[SwitchWithNewTypes](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#19_SwitchWithNewTypes "Include Percentile Die type")] +```csharp +public static int SumPositiveNumbers(IEnumerable sequence) +{ + int sum = 0; + foreach (var i in sequence) + { + switch (i) + { + case 0: + break; + case IEnumerable childSequence: + { + foreach(var item in childSequence) + sum += (item > 0) ? item : 0; + break; + } + case int n when n > 0: + sum += n; + break; + null: + throw new NullReferenceException("Null found in sequence"); + default: + throw new InvalidOperationException("Unrecognized type"); + } + } + return sum; +} +``` -The new syntax for pattern matching expressions makes it easier to create -dispatch algorithms based on an object's type, or other properties, using -a clear and concise syntax. Pattern matching expressions enable these -constructs on data types that are unrelated by inheritance. +- `case 0:` is the familiar constant pattern. +- `case IEnumerable childSequence:` is a type pattern. +- `case int n when n > 0:` is a type pattern with an additional `when` condition. +- `case null:` is the null pattern. +- `default:` is the familiar default case. -You can learn more about pattern matching in the topic +You can learn more about pattern matching in the article dedicated to [pattern matching in C#](../pattern-matching.md). ## Ref locals and returns @@ -315,92 +199,29 @@ dedicated to [pattern matching in C#](../pattern-matching.md). This feature enables algorithms that use and return references to variables defined elsewhere. One example is working with large matrices, and finding a single location with certain -characteristics. One method would return the two indices for -a single location in the matrix: - -[!code-csharp[FindReturningIndices](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] +characteristics. The following method returns a **reference** to that storage in the matrix: -There are many issues with this code. First of all, it's a public -method that's returning a tuple. The language supports this, but -user defined types (either classes or structs) are preferred -for public APIs. +[!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] -Second, this method is returning the indices to the item in the matrix. -That leads callers to write code that uses those indices to dereference -the matrix and modify a single element: +You can declare the return value as a `ref` and modify that value in the matrix, as shown in the following code: -[!code-csharp[UpdateItemFromIndices](../../../samples/snippets/csharp/new-in-7/program.cs#21_UpdateItemFromIndices "Update Item From Indices")] +[!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] -You'd rather write a method that returns a *reference* -to the element of the matrix that you want to change. You could only accomplish -this by using unsafe code and returning a pointer to an `int` in previous versions. - -Let's walk through a series of changes to demonstrate the ref local feature -and show how to create a method that returns a reference to internal storage. -Along the way, you'll learn the rules of the ref return and ref local feature that -protects you from accidentally misusing it. - -Start by modifying the `Find` method declaration so that it returns a `ref int` -instead of a tuple. Then, modify the return statement so it returns the value -stored in the matrix instead of the two indices: - -```csharp -// Note that this won't compile. -// Method declaration indicates ref return, -// but return statement specifies a value return. -public static ref int Find2(int[,] matrix, Func predicate) -{ - for (int i = 0; i < matrix.GetLength(0); i++) - for (int j = 0; j < matrix.GetLength(1); j++) - if (predicate(matrix[i, j])) - return matrix[i, j]; - throw new InvalidOperationException("Not found"); -} -``` - -When you declare that a method returns a `ref` variable, you must also -add the `ref` keyword to each return statement. That indicates return -by reference, and helps developers reading the code later remember that -the method returns by reference: - -[!code-csharp[FindReturningRef](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] - -Now that the method returns a reference to the integer value in the -matrix, you need to modify where it's called. The `var` declaration -means that `valItem` is now an `int` rather than a tuple: - -[!code-csharp[AssignRefReturnToValue](../../../samples/snippets/csharp/new-in-7/program.cs#23_AssignRefReturnToValue "Assign ref return to value")] - -The second `WriteLine` statement in the example above prints out the value `42`, -not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` -keyword enables the compiler to specify the type, but will not implicitly -add the `ref` modifier. Instead, the value referred to by the `ref return` -is *copied* to the variable on the left-hand side of the assignment. The -variable is not a `ref` local. - -In order to get the result you want, you need to add the `ref` modifier -to the local variable declaration to make the variable a reference when -the return value is a reference: - -[!code-csharp[AssignRefReturn](../../../samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] - -Now, the second `WriteLine` statement in the example above will print -out the value `24`, indicating that the storage in the matrix has been -modified. The local variable has been declared with the `ref` modifier, -and it will take a `ref` return. You must initialize a `ref` variable when -it is declared, you cannot split the declaration and the initialization. - -The C# language has three other rules that protect you from misusing +The C# language has several rules that protect you from misusing the `ref` locals and returns: -* You cannot assign a standard method return value to a `ref` local variable. +* You must add the `ref` keyword to the method signature, and to all `return` statements in a method. + - That makes it clear the method returns by reference throughout the method. +* A `ref return` may be assigned to a value variable, or a `ref` variable. + - The return value is copied into a variable, a `ref` variable refers to the same storage. +* You can't assign a standard method return value to a `ref` local variable. - That disallows statements like `ref int i = sequence.Count();` -* You cannot return a `ref` to a variable whose lifetime does not extend beyond the execution of the method. - - That means you cannot return a reference to a local variable or a variable with a similar scope. +* You can't return a `ref` to a variable whose lifetime doesn't extend beyond the execution of the method. + - That means you can't return a reference to a local variable or a variable with a similar scope. * `ref` locals and returns can't be used with async methods. - The compiler can't know if the referenced variable has been set to its final value when the async method returns. -The addition of ref locals and ref returns enable algorithms that are more +The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times. @@ -412,65 +233,26 @@ For more information, see the [ref keyword](../language-reference/keywords/ref.m Many designs for classes include methods that are called from only one location. These additional private methods keep each method small -and focused. However, they can make it harder to understand a class -when reading it the first time. These methods must be understood -outside of the context of the single calling location. - -For those designs, *local functions* enable you to declare methods -inside the context of another method. This makes it easier for readers +and focused. *Local functions* enable you to declare methods +inside the context of another method. Local functions make it easier for readers of the class to see that the local method is only called from the context in which is it declared. -There are two very common use cases for local functions: public iterator +There are two common use cases for local functions: public iterator methods and public async methods. Both types of methods generate code that reports errors later than programmers might expect. In -the case of iterator methods, any exceptions are observed only -when calling code that enumerates the returned sequence. In the case -of async methods, any exceptions are only observed when the returned -`Task` is awaited. +iterator methods, any exceptions are observed only +when calling code that enumerates the returned sequence. In +async methods, any exceptions are only observed when the returned +`Task` is awaited. The following example demonstrates separating parameter validation from the iterator implementation using a local function: -Let's start with an iterator method: - -[!code-csharp[IteratorMethod](../../../samples/snippets/csharp/new-in-7/Iterator.cs#25_IteratorMethod "Iterator method")] - -Examine the code below that calls the iterator method incorrectly: - -[!code-csharp[CallIteratorMethod](../../../samples/snippets/csharp/new-in-7/program.cs#26_CallIteratorMethod "Call iterator method")] - -The exception is thrown when `resultSet` is iterated, not when `resultSet` is created. -In this contained example, most developers could quickly diagnose the -problem. However, in larger codebases, the code that creates an iterator -often isn't as close to the code that enumerates the result. You can -refactor the code so that the public method validates all arguments, -and a private method generates the enumeration: - -[!code-csharp[IteratorMethodRefactored](../../../samples/snippets/csharp/new-in-7/Iterator.cs#27_IteratorMethodRefactored "Iterator method refactored")] - -This refactored version will throw exceptions immediately because the public -method is not an iterator method; only the private method uses the -`yield return` syntax. However, there are potential problems with this -refactoring. The private method should only be called from the public -interface method, because otherwise all argument validation is skipped. -Readers of the class must discover this fact by reading the entire class -and searching for any other references to the `alphabetSubsetImplementation` -method. - -You can make that design intent more clear by declaring the -`alphabetSubsetImplementation` as a local function inside the public -API method: - -[!code-csharp[22_IteratorMethodLocal](../../../samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] - -The version above makes it clear that the local method is referenced -only in the context of the outer method. The rules for local functions -also ensure that a developer can't accidentally call the local function -from another location in the class and bypass the argument validation. +[!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] The same technique can be employed with `async` methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins: -[!code-csharp[TaskExample](../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] +[!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] > [!NOTE] > Some of the designs that are supported by local functions @@ -505,32 +287,13 @@ Changing a method to an expression bodied member is a [binary compatible change] ## Throw expressions In C#, `throw` has always been a statement. Because `throw` is a statement, -not an expression, there were C# constructs where you could not use it. These +not an expression, there were C# constructs where you couldn't use it. These included conditional expressions, null coalescing expressions, and some lambda expressions. The addition of expression-bodied members adds more locations where `throw` expressions would be useful. So that you can write any of these -constructs, C# 7.0 introduces *throw expressions*. - -The syntax is the same as you've always used for `throw` statements. The only difference -is that now you can place them in new locations, such as in a conditional expression: - -[!code-csharp[Throw_ExpressionExample](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#37_Throw_ExpressionExample "conditional throw expressions")] - -This features enables using throw expressions in initialization expressions: +constructs, C# 7.0 introduces *throw expressions*. -[!code-csharp[ThrowInInitialization](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#38_ThrowInInitialization "conditional throw expressions")] - -Previously, those initializations would need to be in a constructor, with the -throw statements in the body of the constructor: - - -[!code-csharp[ThrowInConstructor](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#39_ThrowInConstructor "throw statements")] - -> [!NOTE] -> Both of the preceding constructs will cause exceptions to be thrown during -> the construction of an object. Those are often difficult to recover from. -> For that reason, designs that throw exceptions during construction are -> discouraged. +This addition makes it easier to write more expression-based code. You don't need additional statements for error checking. ## Generalized async return types @@ -540,10 +303,9 @@ type, so using it means allocating an object. In cases where a method declared with the `async` modifier returns a cached result, or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. It can become -very costly if those allocations occur in tight loops. +costly if those allocations occur in tight loops. -The new language feature means that async methods may return other -types in addition to `Task`, `Task` and `void`. The returned type +The new language feature means that async method return types aren't limited to `Task`, `Task`, and `void`. The returned type must still satisfy the async pattern, meaning a `GetAwaiter` method must be accessible. As one concrete example, the `ValueTask` type has been added to the .NET framework to make use of this new language @@ -555,52 +317,32 @@ feature: > You need to add the NuGet package [`System.Threading.Tasks.Extensions`](https://www.nuget.org/packages/System.Threading.Tasks.Extensions/) > in order to use the type. -A simple optimization would be to use `ValueTask` in places where -`Task` would be used before. However, if you want to perform extra -optimizations by hand, you can cache results from async work and -reuse the result in subsequent calls. The `ValueTask` struct has a constructor -with a `Task` parameter so that you can construct a `ValueTask` from the -return value of any existing async method: - -[!code-csharp[AsyncOptimizedValueTask](../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#31_AsyncOptimizedValueTask "Return async result or cached value")] - -As with all performance recommendations, you should benchmark -both versions before making large scale changes to your code. - -When the return value is the target of an `await` statement, changing an API from a to a is a [source compatible change](version-update-considerations.md#source-compatible-changes). In general, changing to `ValueTask` is not. +This enhancement is most useful for library authors to avoid allocating a `Task` in performance critical code. ## Numeric literal syntax improvements Misreading numeric constants can make it harder to understand -code when reading it for the first time. This often -occurs when those numbers are used as bit masks or other symbolic -rather than numeric values. C# 7.0 includes two new features to -make it easier to write numbers in the most readable fashion +code when reading it for the first time. Bit masks or other symbolic +values are prone to misunderstanding. C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: *binary literals*, and *digit separators*. -For those times when you are creating bit masks, or whenever a +For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary: -[!code-csharp[BinaryConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#32_BinaryConstants "Binary constants")] +[!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] The `0b` at the beginning of the constant indicates that the -number is written as a binary number. - -Binary numbers can get very long, so it's often easier to see -the bit patterns by introducing the `_` as a digit separator: - -[!code-csharp[ThousandSeparators](../../../samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] - -The digit separator can appear anywhere in the constant. For base 10 +number is written as a binary number. Binary numbers can get long, so it's often easier to see +the bit patterns by introducing the `_` as a digit separator, as shown above in the binary constant. The digit separator can appear anywhere in the constant. For base 10 numbers, it would be common to use it as a thousands separator: -[!code-csharp[LargeIntegers](../../../samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] +[!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] -The digit separator can be used with `decimal`, `float` and `double` +The digit separator can be used with `decimal`, `float`, and `double` types as well: -[!code-csharp[OtherConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] +[!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] Taken together, you can declare numeric constants with much more readability. diff --git a/docs/toc.yml b/docs/toc.yml index 77d853bd757e8..1bb0b053ca7e2 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -527,6 +527,8 @@ href: csharp/tutorials/intro-to-csharp/introduction-to-classes.md - name: Explore C# 6 href: csharp/tutorials/exploration/csharp-6.yml + - name: Explore C# 7 + href: csharp/tutorials/exploration/csharp-7.yml - name: Work with nullable reference types href: csharp/tutorials/nullable-reference-types.md - name: Upgrade an app to nullable reference types From ab83fffcc9d0cdc041b92c5cf4b153806df6dc69 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Mar 2019 15:11:09 -0400 Subject: [PATCH 02/18] add interactive tutorial --- .../csharp/tutorials/exploration/csharp-7.yml | 536 ++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 docs/csharp/tutorials/exploration/csharp-7.yml diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml new file mode 100644 index 0000000000000..de3a7c3f355bb --- /dev/null +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -0,0 +1,536 @@ +### YamlMime:Tutorial +title: Explore C# 7 - C# interactive C# tutorial +metadata: + title: Explore C# 7 - Try the new features in C# 6 interactively, using your browser + description: In this tutorial, you'll use your browser to explore C# 6 interactively. You'll explore the new idioms you can use with C# 6 that enable more concise and readable code. + audience: Developer + level: intermediate + ms.date: 03/20/2019 + displayType: two-column + interactive: csharp +items: +- durationInMinutes: 1 + content: | + This tutorial lets you explore C# 7 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 6 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. + +- title: Our variable declarations at the assignment location + durationInMinutes: 2 + content: | + The existing syntax that supports `out` parameters has been improved + in this version. + + Previously, you would need to separate the declaration of the out variable + and its initialization into two different statements: + + [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#03_OutVariableOldStyle "classic out variable declaration")] + + You can now declare `out` variables in the argument list of a method call, + rather than writing a separate declaration statement: + + [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] + + You may want to specify the type of the `out` variable for clarity, + as shown above. However, the language does support using an implicitly + typed local variable: + + [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] + + * The code is easier to read. + - You declare the out variable where you use it, not on another line above. + * No need to assign an initial value. + - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. + + The most common use for this feature will be the `Try` pattern. In this + pattern, a method returns a `bool` indicating success or failure and an + `out` variable that provides the result if the method succeeds. + + When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards: + + ```csharp + if (!int.TryParse(input, out int result)) + { + return null; + } + + return result; + ``` + +- title: Tuples create light-weight data structures + durationInMinutes: 1 + content: | + > [!NOTE] + > The new tuples features require the types. + > You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it + > on platforms that do not include the types. + > + > This is similar to other language features that rely on types + > delivered in the framework. Example include `async` and `await` + > relying on the `INotifyCompletion` interface, and LINQ relying + > on `IEnumerable`. However, the delivery mechanism is changing + > as .NET is becoming more platform independent. The .NET Framework + > may not always ship on the same cadence as the language compiler. When new language + > features rely on new types, those types will be available as NuGet packages when + > the language features ship. As these new types get added to the .NET Standard + > API and delivered as part of the framework, the NuGet package requirement will + > be removed. + + C# provides a rich syntax for classes and structs that is used to explain + your design intent. But sometimes that rich syntax requires extra + work with minimal benefit. You may often write methods that need a simple + structure containing more than one data element. To support these scenarios + *tuples* were added to C#. Tuples are lightweight data structures + that contain multiple fields to represent the data members. + The fields are not validated, and you cannot define your own methods + + > [!NOTE] + > Tuples were available before C# 7.0, + > but they were inefficient and had no language support. + > This meant that tuple elements could only be referenced as + > `Item1`, `Item2` and so on. C# 7.0 introduces language support for tuples, + > which enables semantic names for the fields of a tuple using new, + > more efficient tuple types. + + You can create a tuple by assigning a value to each member: + + [!code-csharp[UnnamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] + + That assignment creates a tuple whose members are `Item1` and `Item2`, + which you can use in the same way as + You can change the syntax to create a tuple that provides semantic + names to each of the members of the tuple: + + [!code-csharp[NamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] + + The `namedLetters` tuple contains fields referred to as `Alpha` and + `Beta`. Those names exist only at compile time and are not preserved + for example when inspecting the tuple using reflection at runtime. + + In a tuple assignment, you can also specify the names of the fields + on the right-hand side of the assignment: + + [!code-csharp[ImplicitNamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] + + You can specify names for the fields on both the + left and right-hand side of the assignment: + + [!code-csharp[NamedTupleConflict](../../../samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] + + The line above generates a warning, `CS8123`, telling you that the names on the right + side of the assignment, `Alpha` and `Beta` are ignored because they conflict + with the names on the left side, `First` and `Second`. + + The examples above show the basic syntax to declare tuples. Tuples are + most useful as return types for `private` and `internal` methods. Tuples + provide a simple syntax for those methods to return multiple discrete values: + You save the work of authoring a `class` or a `struct` that + defines the type returned. There is no need for creating a new type. + + Creating a tuple is more efficient and more productive. + It is a simpler, lightweight syntax to define a data structure that carries + more than one value. The example method below returns the minimum and maximum + values found in a sequence of integers: + + [!code-csharp[TupleReturningMethod](../../../samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] + + Using tuples in this way offers several advantages: + + * You save the work of authoring a `class` or a `struct` that defines the type returned. + * You do not need to create new type. + * The language enhancements removes the need to call the methods. + + The declaration for the method provides the names for the fields of the + tuple that is returned. When you call the method, the return value is a + tuple whose fields are `Max` and `Min`: + + [!code-csharp[CallingTupleMethod](../../../samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] + + There may be times when you want to unpackage the members of a tuple that + were returned from a method. You can do that by declaring separate variables + for each of the values in the tuple. This is called *deconstructing* the tuple: + + [!code-csharp[CallingWithDeconstructor](../../../samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] + + You can also provide a similar deconstruction for any type in .NET. This is + done by writing a `Deconstruct` method as a member of the class. That + `Deconstruct` method provides a set of `out` arguments for each of the + properties you want to extract. Consider + this `Point` class that provides a deconstructor method that extracts + the `X` and `Y` coordinates: + + [!code-csharp[PointWithDeconstruction](../../../samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] + + You can extract the individual fields by assigning a `Point` to a tuple: + + [!code-csharp[DeconstructPoint](../../../samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] + + You are not bound by the names defined in the `Deconstruct` method. You + can rename the extract variables as part of the assignment: + + [!code-csharp[DeconstructNames](../../../samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] + + You can learn more in depth about tuples in the + [tuples topic](../tuples.md). + +- title: Use discards to select needed members + durationInMinutes: 2 + content: | + 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. + + Discards are supported in the following scenarios: + + * When deconstructing tuples or user-defined types. + + * When calling methods with [out](../language-reference/keywords/out-parameter-modifier.md) parameters. + + * In a pattern matching operation with the [is](../language-reference/keywords/is.md) and [switch](../language-reference/keywords/switch.md) statements. + + * As a standalone identifier when you want to explicitly identify the value of an assignment as a discard. + + 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. + + [!code-csharp[Tuple-discard](../../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] + + For more information, see [Discards](../discards.md). + +- title: Use the type pattern with the is expression + durationInMinutes: 2 + content: | + *Pattern matching* is a feature that allows you to implement method dispatch on + properties other than the type of an object. You're probably already familiar + with method dispatch based on the type of an object. In Object Oriented programming, + virtual and override methods provide language syntax to implement method dispatching + based on an object's type. Base and Derived classes provide different implementations. + Pattern matching expressions extend this concept so that you can easily + implement similar dispatch patterns for types and data elements that are + not related through an inheritance hierarchy. + + Pattern matching supports `is` expressions and `switch` expressions. Each + enables inspecting an object and its properties to determine if that object + satisfies the sought pattern. You use the `when` keyword to specify additional + rules to the pattern. + + ### `is` expression + + The `is` pattern expression extends the familiar [`is` operator](../language-reference/keywords/is.md#pattern-matching-with-is) to query an object beyond its type. + + Let's start with a simple scenario. We'll add capabilities to this scenario + that demonstrate how pattern matching expressions make algorithms that work + with unrelated types easy. We'll start with a method that computes the sum + of a number of die rolls: + + [!code-csharp[SumDieRolls](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#14_SumDieRolls "Sum die rolls")] + + You might quickly find that you need to find the sum of die rolls where + some of the rolls are made with multiple dice (dice is the plural of die). Part of the input + sequence may be multiple results instead of a single number: + + [!code-csharp[SumDieRollsWithGroups](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#15_SumDieRollsWithGroups "Sum die rolls with groups")] + + The `is` pattern expression works quite well in this scenario. As part of + checking the type, you write a variable initialization. This creates + a new variable of the validated runtime type. + + As you keep extending these scenarios, you may find that you build more + `if` and `else if` statements. Once that becomes unwieldy, you'll likely + want to switch to `switch` pattern expressions. + +- title: Pattern matching in the switch statement + durationInMinutes: 2 + content: | + The *match expression* has a familiar syntax, based on the `switch` + statement already part of the C# language. Let's translate the existing code + to use a match expression before adding new cases: + + [!code-csharp[SumUsingSwitch](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#16_SumUsingSwitch "Sum using switch")] + + The match expressions have a slightly different syntax than the `is` expressions, where + you declare the type and variable at the beginning of the `case` expression. + + The match expressions also support constants. This can save time by + factoring out simple cases: + + [!code-csharp[SwitchWithConstants](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#17_SwitchWithConstants "Switch with constants")] + + The code above adds cases for `0` as a special case of `int`, and `null` + as a special case when there is no input. This demonstrates one important + new feature in switch pattern expressions: the order of the `case` + expressions now matters. The `0` case must appear before the general `int` + case. Otherwise, the first pattern to match would be the `int` case, + even when the value is `0`. If you accidentally order match expressions such + that a later case has already been handled, the compiler will flag that + and generate an error. + + This same behavior enables the special case for an empty input sequence. + You can see that the case for an `IEnumerable` item that has elements + must appear before the general `IEnumerable` case. + + This version has also added a `default` case. The `default` case is always + evaluated last, regardless of the order it appears in the source. For that + reason, convention is to put the `default` case last. + + Finally, let's add one last `case` for a new style of die. Some games + use percentile dice to represent larger ranges of numbers. + + > [!NOTE] + > Two 10-sided percentile dice can represent every number from 0 + > through 99. One die has sides labelled `00`, `10`, `20`, ... `90`. The other + > die has sides labeled `0`, `1`, `2`, ... `9`. Add the two die values + > together and you can get every number from 0 through 99. + + To add this kind of die to your collection, first define a type to represent + the percentile dice. The `TensDigit` property stores values `0`, `10`, `20`, up to `90`: + + [!code-csharp[18_PercentileDice](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#18_PercentileDice "Percentile Die type")] + + Then, add a `case` match expression for the new type: + + [!code-csharp[SwitchWithNewTypes](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#19_SwitchWithNewTypes "Include Percentile Die type")] + + The new syntax for pattern matching expressions makes it easier to create + dispatch algorithms based on an object's type, or other properties, using + a clear and concise syntax. Pattern matching expressions enable these + constructs on data types that are unrelated by inheritance. + + You can learn more about pattern matching in the topic + dedicated to [pattern matching in C#](../pattern-matching.md). + +- title: Optimize memory storage using ref locals and returns + durationInMinutes: 2 + content: | + This feature enables algorithms that use and return references + to variables defined elsewhere. One example is working with + large matrices, and finding a single location with certain + characteristics. One method would return the two indices for + a single location in the matrix: + + [!code-csharp[FindReturningIndices](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] + + There are many issues with this code. First of all, it's a public + method that's returning a tuple. The language supports this, but + user defined types (either classes or structs) are preferred + for public APIs. + + Second, this method is returning the indices to the item in the matrix. + That leads callers to write code that uses those indices to dereference + the matrix and modify a single element: + + [!code-csharp[UpdateItemFromIndices](../../../samples/snippets/csharp/new-in-7/program.cs#21_UpdateItemFromIndices "Update Item From Indices")] + + You'd rather write a method that returns a *reference* + to the element of the matrix that you want to change. You could only accomplish + this by using unsafe code and returning a pointer to an `int` in previous versions. + + Let's walk through a series of changes to demonstrate the ref local feature + and show how to create a method that returns a reference to internal storage. + Along the way, you'll learn the rules of the ref return and ref local feature that + protects you from accidentally misusing it. + + Start by modifying the `Find` method declaration so that it returns a `ref int` + instead of a tuple. Then, modify the return statement so it returns the value + stored in the matrix instead of the two indices: + + ```csharp + // Note that this won't compile. + // Method declaration indicates ref return, + // but return statement specifies a value return. + public static ref int Find2(int[,] matrix, Func predicate) + { + for (int i = 0; i < matrix.GetLength(0); i++) + for (int j = 0; j < matrix.GetLength(1); j++) + if (predicate(matrix[i, j])) + return matrix[i, j]; + throw new InvalidOperationException("Not found"); + } + ``` + + When you declare that a method returns a `ref` variable, you must also + add the `ref` keyword to each return statement. That indicates return + by reference, and helps developers reading the code later remember that + the method returns by reference: + + [!code-csharp[FindReturningRef](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] + + Now that the method returns a reference to the integer value in the + matrix, you need to modify where it's called. The `var` declaration + means that `valItem` is now an `int` rather than a tuple: + + [!code-csharp[AssignRefReturnToValue](../../../samples/snippets/csharp/new-in-7/program.cs#23_AssignRefReturnToValue "Assign ref return to value")] + + The second `WriteLine` statement in the example above prints out the value `42`, + not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` + keyword enables the compiler to specify the type, but will not implicitly + add the `ref` modifier. Instead, the value referred to by the `ref return` + is *copied* to the variable on the left-hand side of the assignment. The + variable is not a `ref` local. + + In order to get the result you want, you need to add the `ref` modifier + to the local variable declaration to make the variable a reference when + the return value is a reference: + + [!code-csharp[AssignRefReturn](../../../samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] + + Now, the second `WriteLine` statement in the example above will print + out the value `24`, indicating that the storage in the matrix has been + modified. The local variable has been declared with the `ref` modifier, + and it will take a `ref` return. You must initialize a `ref` variable when + it is declared, you cannot split the declaration and the initialization. + + The C# language has three other rules that protect you from misusing + the `ref` locals and returns: + + * You cannot assign a standard method return value to a `ref` local variable. + - That disallows statements like `ref int i = sequence.Count();` + * You cannot return a `ref` to a variable whose lifetime does not extend beyond the execution of the method. + - That means you cannot return a reference to a local variable or a variable with a similar scope. + * `ref` locals and returns can't be used with async methods. + - The compiler can't know if the referenced variable has been set to its final value when the async method returns. + + The addition of ref locals and ref returns enable algorithms that are more + efficient by avoiding copying values, or performing dereferencing operations + multiple times. + + Adding `ref` to the return value is a [source compatible change](version-update-considerations.md#source-compatible-changes). Existing code compiles, but the ref return value is copied when assigned. Callers must update the storage for the return value to a `ref` local variable to store the return as a reference. + + For more information, see the [ref keyword](../language-reference/keywords/ref.md) article. + +- title: Minimize access to code with local functions + durationInMinutes: 2 + content: | + Many designs for classes include methods that are called from only + one location. These additional private methods keep each method small + and focused. However, they can make it harder to understand a class + when reading it the first time. These methods must be understood + outside of the context of the single calling location. + + For those designs, *local functions* enable you to declare methods + inside the context of another method. This makes it easier for readers + of the class to see that the local method is only called from the context + in which is it declared. + + There are two very common use cases for local functions: public iterator + methods and public async methods. Both types of methods generate + code that reports errors later than programmers might expect. In + the case of iterator methods, any exceptions are observed only + when calling code that enumerates the returned sequence. In the case + of async methods, any exceptions are only observed when the returned + `Task` is awaited. + + Let's start with an iterator method: + + [!code-csharp[IteratorMethod](../../../samples/snippets/csharp/new-in-7/Iterator.cs#25_IteratorMethod "Iterator method")] + + Examine the code below that calls the iterator method incorrectly: + + [!code-csharp[CallIteratorMethod](../../../samples/snippets/csharp/new-in-7/program.cs#26_CallIteratorMethod "Call iterator method")] + + The exception is thrown when `resultSet` is iterated, not when `resultSet` is created. + In this contained example, most developers could quickly diagnose the + problem. However, in larger codebases, the code that creates an iterator + often isn't as close to the code that enumerates the result. You can + refactor the code so that the public method validates all arguments, + and a private method generates the enumeration: + + [!code-csharp[IteratorMethodRefactored](../../../samples/snippets/csharp/new-in-7/Iterator.cs#27_IteratorMethodRefactored "Iterator method refactored")] + + This refactored version will throw exceptions immediately because the public + method is not an iterator method; only the private method uses the + `yield return` syntax. However, there are potential problems with this + refactoring. The private method should only be called from the public + interface method, because otherwise all argument validation is skipped. + Readers of the class must discover this fact by reading the entire class + and searching for any other references to the `alphabetSubsetImplementation` + method. + + You can make that design intent more clear by declaring the + `alphabetSubsetImplementation` as a local function inside the public + API method: + + [!code-csharp[22_IteratorMethodLocal](../../../samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] + + The version above makes it clear that the local method is referenced + only in the context of the outer method. The rules for local functions + also ensure that a developer can't accidentally call the local function + from another location in the class and bypass the argument validation. + + The same technique can be employed with `async` methods to ensure that + exceptions arising from argument validation are thrown before the asynchronous + work begins: + + [!code-csharp[TaskExample](../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] + + > [!NOTE] + > Some of the designs that are supported by local functions + > could also be accomplished using *lambda expressions*. Those + > interested can [read more about the differences](../local-functions-vs-lambdas.md) + +- title: Throw expressions more concise error reporting + durationInMinutes: 2 + content: | + In C#, `throw` has always been a statement. Because `throw` is a statement, + not an expression, there were C# constructs where you could not use it. These + included conditional expressions, null coalescing expressions, and some lambda + expressions. The addition of expression-bodied members adds more locations + where `throw` expressions would be useful. So that you can write any of these + constructs, C# 7.0 introduces *throw expressions*. + + The syntax is the same as you've always used for `throw` statements. The only difference + is that now you can place them in new locations, such as in a conditional expression: + + [!code-csharp[Throw_ExpressionExample](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#37_Throw_ExpressionExample "conditional throw expressions")] + + This features enables using throw expressions in initialization expressions: + + [!code-csharp[ThrowInInitialization](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#38_ThrowInInitialization "conditional throw expressions")] + + Previously, those initializations would need to be in a constructor, with the + throw statements in the body of the constructor: + + + [!code-csharp[ThrowInConstructor](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#39_ThrowInConstructor "throw statements")] + + > [!NOTE] + > Both of the preceding constructs will cause exceptions to be thrown during + > the construction of an object. Those are often difficult to recover from. + > For that reason, designs that throw exceptions during construction are + > discouraged. + +- title: More readable numeric literals + durationInMinutes: 2 + content: | + Misreading numeric constants can make it harder to understand + code when reading it for the first time. This often + occurs when those numbers are used as bit masks or other symbolic + rather than numeric values. C# 7.0 includes two new features to + make it easier to write numbers in the most readable fashion + for the intended use: *binary literals*, and *digit separators*. + + For those times when you are creating bit masks, or whenever a + binary representation of a number makes the most readable code, + write that number in binary: + + [!code-csharp[BinaryConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#32_BinaryConstants "Binary constants")] + + The `0b` at the beginning of the constant indicates that the + number is written as a binary number. + + Binary numbers can get very long, so it's often easier to see + the bit patterns by introducing the `_` as a digit separator: + + [!code-csharp[ThousandSeparators](../../../samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] + + The digit separator can appear anywhere in the constant. For base 10 + numbers, it would be common to use it as a thousands separator: + + [!code-csharp[LargeIntegers](../../../samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] + + The digit separator can be used with `decimal`, `float` and `double` + types as well: + + [!code-csharp[OtherConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] + + Taken together, you can declare numeric constants with much more + readability. + +- content: | + You've completed an exploration of the new features in C# 6. Now try them yourself in your applications. + From 7228ac4fdfda7fb68793d30355f4be3ea2843674 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Mar 2019 16:17:38 -0400 Subject: [PATCH 03/18] fix the build errors --- .../csharp/tutorials/exploration/csharp-7.yml | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index de3a7c3f355bb..fda2c7c182c3d 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -92,14 +92,14 @@ items: You can create a tuple by assigning a value to each member: - [!code-csharp[UnnamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] + [!code-csharp[UnnamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] That assignment creates a tuple whose members are `Item1` and `Item2`, which you can use in the same way as You can change the syntax to create a tuple that provides semantic names to each of the members of the tuple: - [!code-csharp[NamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] + [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved @@ -108,12 +108,12 @@ items: In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: - [!code-csharp[ImplicitNamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] + [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] You can specify names for the fields on both the left and right-hand side of the assignment: - [!code-csharp[NamedTupleConflict](../../../samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] + [!code-csharp[NamedTupleConflict](~/samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] The line above generates a warning, `CS8123`, telling you that the names on the right side of the assignment, `Alpha` and `Beta` are ignored because they conflict @@ -130,7 +130,7 @@ items: more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: - [!code-csharp[TupleReturningMethod](../../../samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] + [!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] Using tuples in this way offers several advantages: @@ -142,13 +142,13 @@ items: tuple that is returned. When you call the method, the return value is a tuple whose fields are `Max` and `Min`: - [!code-csharp[CallingTupleMethod](../../../samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] + [!code-csharp[CallingTupleMethod](~/samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This is called *deconstructing* the tuple: - [!code-csharp[CallingWithDeconstructor](../../../samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] + [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] You can also provide a similar deconstruction for any type in .NET. This is done by writing a `Deconstruct` method as a member of the class. That @@ -157,16 +157,16 @@ items: this `Point` class that provides a deconstructor method that extracts the `X` and `Y` coordinates: - [!code-csharp[PointWithDeconstruction](../../../samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] + [!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] You can extract the individual fields by assigning a `Point` to a tuple: - [!code-csharp[DeconstructPoint](../../../samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] + [!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] You are not bound by the names defined in the `Deconstruct` method. You can rename the extract variables as part of the assignment: - [!code-csharp[DeconstructNames](../../../samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] + [!code-csharp[DeconstructNames](~/samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] You can learn more in depth about tuples in the [tuples topic](../tuples.md). @@ -188,7 +188,7 @@ items: 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. - [!code-csharp[Tuple-discard](../../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] + [!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] For more information, see [Discards](../discards.md). @@ -218,13 +218,13 @@ items: with unrelated types easy. We'll start with a method that computes the sum of a number of die rolls: - [!code-csharp[SumDieRolls](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#14_SumDieRolls "Sum die rolls")] + [!code-csharp[SumDieRolls](~/samples/snippets/csharp/new-in-7/patternmatch.cs#14_SumDieRolls "Sum die rolls")] You might quickly find that you need to find the sum of die rolls where some of the rolls are made with multiple dice (dice is the plural of die). Part of the input sequence may be multiple results instead of a single number: - [!code-csharp[SumDieRollsWithGroups](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#15_SumDieRollsWithGroups "Sum die rolls with groups")] + [!code-csharp[SumDieRollsWithGroups](~/samples/snippets/csharp/new-in-7/patternmatch.cs#15_SumDieRollsWithGroups "Sum die rolls with groups")] The `is` pattern expression works quite well in this scenario. As part of checking the type, you write a variable initialization. This creates @@ -241,7 +241,7 @@ items: statement already part of the C# language. Let's translate the existing code to use a match expression before adding new cases: - [!code-csharp[SumUsingSwitch](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#16_SumUsingSwitch "Sum using switch")] + [!code-csharp[SumUsingSwitch](~/samples/snippets/csharp/new-in-7/patternmatch.cs#16_SumUsingSwitch "Sum using switch")] The match expressions have a slightly different syntax than the `is` expressions, where you declare the type and variable at the beginning of the `case` expression. @@ -249,7 +249,7 @@ items: The match expressions also support constants. This can save time by factoring out simple cases: - [!code-csharp[SwitchWithConstants](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#17_SwitchWithConstants "Switch with constants")] + [!code-csharp[SwitchWithConstants](~/samples/snippets/csharp/new-in-7/patternmatch.cs#17_SwitchWithConstants "Switch with constants")] The code above adds cases for `0` as a special case of `int`, and `null` as a special case when there is no input. This demonstrates one important @@ -280,11 +280,11 @@ items: To add this kind of die to your collection, first define a type to represent the percentile dice. The `TensDigit` property stores values `0`, `10`, `20`, up to `90`: - [!code-csharp[18_PercentileDice](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#18_PercentileDice "Percentile Die type")] + [!code-csharp[18_PercentileDice](~/samples/snippets/csharp/new-in-7/patternmatch.cs#18_PercentileDice "Percentile Die type")] Then, add a `case` match expression for the new type: - [!code-csharp[SwitchWithNewTypes](../../../samples/snippets/csharp/new-in-7/patternmatch.cs#19_SwitchWithNewTypes "Include Percentile Die type")] + [!code-csharp[SwitchWithNewTypes](~/samples/snippets/csharp/new-in-7/patternmatch.cs#19_SwitchWithNewTypes "Include Percentile Die type")] The new syntax for pattern matching expressions makes it easier to create dispatch algorithms based on an object's type, or other properties, using @@ -303,7 +303,7 @@ items: characteristics. One method would return the two indices for a single location in the matrix: - [!code-csharp[FindReturningIndices](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] + [!code-csharp[FindReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] There are many issues with this code. First of all, it's a public method that's returning a tuple. The language supports this, but @@ -314,7 +314,7 @@ items: That leads callers to write code that uses those indices to dereference the matrix and modify a single element: - [!code-csharp[UpdateItemFromIndices](../../../samples/snippets/csharp/new-in-7/program.cs#21_UpdateItemFromIndices "Update Item From Indices")] + [!code-csharp[UpdateItemFromIndices](~/samples/snippets/csharp/new-in-7/program.cs#21_UpdateItemFromIndices "Update Item From Indices")] You'd rather write a method that returns a *reference* to the element of the matrix that you want to change. You could only accomplish @@ -348,13 +348,13 @@ items: by reference, and helps developers reading the code later remember that the method returns by reference: - [!code-csharp[FindReturningRef](../../../samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] + [!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple: - [!code-csharp[AssignRefReturnToValue](../../../samples/snippets/csharp/new-in-7/program.cs#23_AssignRefReturnToValue "Assign ref return to value")] + [!code-csharp[AssignRefReturnToValue](~/samples/snippets/csharp/new-in-7/program.cs#23_AssignRefReturnToValue "Assign ref return to value")] The second `WriteLine` statement in the example above prints out the value `42`, not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` @@ -367,7 +367,7 @@ items: to the local variable declaration to make the variable a reference when the return value is a reference: - [!code-csharp[AssignRefReturn](../../../samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] + [!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] Now, the second `WriteLine` statement in the example above will print out the value `24`, indicating that the storage in the matrix has been @@ -417,11 +417,11 @@ items: Let's start with an iterator method: - [!code-csharp[IteratorMethod](../../../samples/snippets/csharp/new-in-7/Iterator.cs#25_IteratorMethod "Iterator method")] + [!code-csharp[IteratorMethod](~/samples/snippets/csharp/new-in-7/Iterator.cs#25_IteratorMethod "Iterator method")] Examine the code below that calls the iterator method incorrectly: - [!code-csharp[CallIteratorMethod](../../../samples/snippets/csharp/new-in-7/program.cs#26_CallIteratorMethod "Call iterator method")] + [!code-csharp[CallIteratorMethod](~/samples/snippets/csharp/new-in-7/program.cs#26_CallIteratorMethod "Call iterator method")] The exception is thrown when `resultSet` is iterated, not when `resultSet` is created. In this contained example, most developers could quickly diagnose the @@ -430,7 +430,7 @@ items: refactor the code so that the public method validates all arguments, and a private method generates the enumeration: - [!code-csharp[IteratorMethodRefactored](../../../samples/snippets/csharp/new-in-7/Iterator.cs#27_IteratorMethodRefactored "Iterator method refactored")] + [!code-csharp[IteratorMethodRefactored](~/samples/snippets/csharp/new-in-7/Iterator.cs#27_IteratorMethodRefactored "Iterator method refactored")] This refactored version will throw exceptions immediately because the public method is not an iterator method; only the private method uses the @@ -445,7 +445,7 @@ items: `alphabetSubsetImplementation` as a local function inside the public API method: - [!code-csharp[22_IteratorMethodLocal](../../../samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] + [!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] The version above makes it clear that the local method is referenced only in the context of the outer method. The rules for local functions @@ -456,7 +456,7 @@ items: exceptions arising from argument validation are thrown before the asynchronous work begins: - [!code-csharp[TaskExample](../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] + [!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] > [!NOTE] > Some of the designs that are supported by local functions @@ -476,17 +476,17 @@ items: The syntax is the same as you've always used for `throw` statements. The only difference is that now you can place them in new locations, such as in a conditional expression: - [!code-csharp[Throw_ExpressionExample](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#37_Throw_ExpressionExample "conditional throw expressions")] + [!code-csharp[Throw_ExpressionExample](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#37_Throw_ExpressionExample "conditional throw expressions")] This features enables using throw expressions in initialization expressions: - [!code-csharp[ThrowInInitialization](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#38_ThrowInInitialization "conditional throw expressions")] + [!code-csharp[ThrowInInitialization](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#38_ThrowInInitialization "conditional throw expressions")] Previously, those initializations would need to be in a constructor, with the throw statements in the body of the constructor: - [!code-csharp[ThrowInConstructor](../../../samples/snippets/csharp/new-in-7/throwexpressions.cs#39_ThrowInConstructor "throw statements")] + [!code-csharp[ThrowInConstructor](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#39_ThrowInConstructor "throw statements")] > [!NOTE] > Both of the preceding constructs will cause exceptions to be thrown during @@ -508,7 +508,7 @@ items: binary representation of a number makes the most readable code, write that number in binary: - [!code-csharp[BinaryConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#32_BinaryConstants "Binary constants")] + [!code-csharp[BinaryConstants](~/samples/snippets/csharp/new-in-7/Program.cs#32_BinaryConstants "Binary constants")] The `0b` at the beginning of the constant indicates that the number is written as a binary number. @@ -516,17 +516,17 @@ items: Binary numbers can get very long, so it's often easier to see the bit patterns by introducing the `_` as a digit separator: - [!code-csharp[ThousandSeparators](../../../samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] + [!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] The digit separator can appear anywhere in the constant. For base 10 numbers, it would be common to use it as a thousands separator: - [!code-csharp[LargeIntegers](../../../samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] + [!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] The digit separator can be used with `decimal`, `float` and `double` types as well: - [!code-csharp[OtherConstants](../../../samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] + [!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] Taken together, you can declare numeric constants with much more readability. From 22fcf5f0c522570186a995c13a56bf9321af5c8c Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Mar 2019 17:09:23 -0400 Subject: [PATCH 04/18] build errors, round 2 --- docs/csharp/tutorials/exploration/csharp-7.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index fda2c7c182c3d..0605db402c193 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -169,7 +169,7 @@ items: [!code-csharp[DeconstructNames](~/samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] You can learn more in depth about tuples in the - [tuples topic](../tuples.md). + [tuples topic](../../tuples.md). - title: Use discards to select needed members durationInMinutes: 2 @@ -180,9 +180,9 @@ items: * When deconstructing tuples or user-defined types. - * When calling methods with [out](../language-reference/keywords/out-parameter-modifier.md) parameters. + * When calling methods with [out](../../language-reference/keywords/out-parameter-modifier.md) parameters. - * In a pattern matching operation with the [is](../language-reference/keywords/is.md) and [switch](../language-reference/keywords/switch.md) statements. + * In a pattern matching operation with the [is](../../language-reference/keywords/is.md) and [switch](../../language-reference/keywords/switch.md) statements. * As a standalone identifier when you want to explicitly identify the value of an assignment as a discard. @@ -190,7 +190,7 @@ items: [!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] - For more information, see [Discards](../discards.md). + For more information, see [Discards](../../discards.md). - title: Use the type pattern with the is expression durationInMinutes: 2 @@ -211,7 +211,7 @@ items: ### `is` expression - The `is` pattern expression extends the familiar [`is` operator](../language-reference/keywords/is.md#pattern-matching-with-is) to query an object beyond its type. + The `is` pattern expression extends the familiar [`is` operator](../../language-reference/keywords/is.md#pattern-matching-with-is) to query an object beyond its type. Let's start with a simple scenario. We'll add capabilities to this scenario that demonstrate how pattern matching expressions make algorithms that work @@ -292,7 +292,7 @@ items: constructs on data types that are unrelated by inheritance. You can learn more about pattern matching in the topic - dedicated to [pattern matching in C#](../pattern-matching.md). + dedicated to [pattern matching in C#](../../pattern-matching.md). - title: Optimize memory storage using ref locals and returns durationInMinutes: 2 @@ -391,7 +391,7 @@ items: Adding `ref` to the return value is a [source compatible change](version-update-considerations.md#source-compatible-changes). Existing code compiles, but the ref return value is copied when assigned. Callers must update the storage for the return value to a `ref` local variable to store the return as a reference. - For more information, see the [ref keyword](../language-reference/keywords/ref.md) article. + For more information, see the [ref keyword](../../language-reference/keywords/ref.md) article. - title: Minimize access to code with local functions durationInMinutes: 2 @@ -461,7 +461,7 @@ items: > [!NOTE] > Some of the designs that are supported by local functions > could also be accomplished using *lambda expressions*. Those - > interested can [read more about the differences](../local-functions-vs-lambdas.md) + > interested can [read more about the differences](../../local-functions-vs-lambdas.md) - title: Throw expressions more concise error reporting durationInMinutes: 2 From bfcd7af2130886027802107fc6ecfebaa91a9240 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 Mar 2019 17:26:11 -0400 Subject: [PATCH 05/18] one more build warning --- docs/csharp/tutorials/exploration/csharp-7.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index 0605db402c193..c878430c2e3a2 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -389,7 +389,7 @@ items: efficient by avoiding copying values, or performing dereferencing operations multiple times. - Adding `ref` to the return value is a [source compatible change](version-update-considerations.md#source-compatible-changes). Existing code compiles, but the ref return value is copied when assigned. Callers must update the storage for the return value to a `ref` local variable to store the return as a reference. + Adding `ref` to the return value is a [source compatible change](../../whats-new/version-update-considerations.md#source-compatible-changes). Existing code compiles, but the ref return value is copied when assigned. Callers must update the storage for the return value to a `ref` local variable to store the return as a reference. For more information, see the [ref keyword](../../language-reference/keywords/ref.md) article. From 6774db992ba27dc7466b660679b68dc13d77dc5e Mon Sep 17 00:00:00 2001 From: Ron Petrusha Date: Fri, 22 Mar 2019 09:54:41 -0400 Subject: [PATCH 06/18] Apply suggestions from code review A very thorough early review from Ron Co-Authored-By: BillWagner --- .../csharp/tutorials/exploration/csharp-7.yml | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index c878430c2e3a2..a7d7e5da8a994 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -40,7 +40,7 @@ items: * No need to assign an initial value. - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. - The most common use for this feature will be the `Try` pattern. In this + The most common use for this feature is in the `Try` pattern. In this pattern, a method returns a `bool` indicating success or failure and an `out` variable that provides the result if the method succeeds. @@ -64,20 +64,20 @@ items: > on platforms that do not include the types. > > This is similar to other language features that rely on types - > delivered in the framework. Example include `async` and `await` + > delivered in the framework. Examples include `async` and `await` > relying on the `INotifyCompletion` interface, and LINQ relying > on `IEnumerable`. However, the delivery mechanism is changing > as .NET is becoming more platform independent. The .NET Framework > may not always ship on the same cadence as the language compiler. When new language > features rely on new types, those types will be available as NuGet packages when - > the language features ship. As these new types get added to the .NET Standard - > API and delivered as part of the framework, the NuGet package requirement will + > the language features ship. As these new types are added to the .NET Standard + > Library and delivered as part of the framework, the NuGet package requirement will > be removed. - C# provides a rich syntax for classes and structs that is used to explain + C# provides a rich syntax for classes and structs that explains your design intent. But sometimes that rich syntax requires extra work with minimal benefit. You may often write methods that need a simple - structure containing more than one data element. To support these scenarios + structure containing more than one data element. To support these scenarios, *tuples* were added to C#. Tuples are lightweight data structures that contain multiple fields to represent the data members. The fields are not validated, and you cannot define your own methods @@ -103,7 +103,7 @@ items: The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved - for example when inspecting the tuple using reflection at runtime. +when inspecting the tuple using reflection at runtime, for example. In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: @@ -116,17 +116,17 @@ items: [!code-csharp[NamedTupleConflict](~/samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] The line above generates a warning, `CS8123`, telling you that the names on the right - side of the assignment, `Alpha` and `Beta` are ignored because they conflict + side of the assignment, `Alpha` and `Beta`, are ignored because they conflict with the names on the left side, `First` and `Second`. The examples above show the basic syntax to declare tuples. Tuples are most useful as return types for `private` and `internal` methods. Tuples provide a simple syntax for those methods to return multiple discrete values: You save the work of authoring a `class` or a `struct` that - defines the type returned. There is no need for creating a new type. + defines the type returned. There is no need to create a new type. Creating a tuple is more efficient and more productive. - It is a simpler, lightweight syntax to define a data structure that carries + It has a simpler, lightweight syntax to define a data structure that carries more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: @@ -136,7 +136,7 @@ items: * You save the work of authoring a `class` or a `struct` that defines the type returned. * You do not need to create new type. - * The language enhancements removes the need to call the methods. + * The language enhancements removes the need to call the methods. The declaration for the method provides the names for the fields of the tuple that is returned. When you call the method, the return value is a @@ -174,7 +174,7 @@ items: - title: Use discards to select needed members durationInMinutes: 2 content: | - 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. + 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, the discard can't be used in code. Discards are supported in the following scenarios: @@ -186,7 +186,7 @@ items: * As a standalone identifier when you want to explicitly identify the value of an assignment as a discard. - 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. + The following example defines a `QueryCityDataForYears` method that returns a 6-tuple that contains 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. [!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] @@ -197,16 +197,16 @@ items: content: | *Pattern matching* is a feature that allows you to implement method dispatch on properties other than the type of an object. You're probably already familiar - with method dispatch based on the type of an object. In Object Oriented programming, + with method dispatch based on the type of an object. In object oriented programming, virtual and override methods provide language syntax to implement method dispatching - based on an object's type. Base and Derived classes provide different implementations. + based on an object's type. Base and derived classes provide different implementations. Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that are not related through an inheritance hierarchy. Pattern matching supports `is` expressions and `switch` expressions. Each enables inspecting an object and its properties to determine if that object - satisfies the sought pattern. You use the `when` keyword to specify additional + satisfies the desired pattern. You use the `when` keyword to specify additional rules to the pattern. ### `is` expression @@ -299,18 +299,18 @@ items: content: | This feature enables algorithms that use and return references to variables defined elsewhere. One example is working with - large matrices, and finding a single location with certain + large matrices and finding a single location with certain characteristics. One method would return the two indices for a single location in the matrix: [!code-csharp[FindReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] There are many issues with this code. First of all, it's a public - method that's returning a tuple. The language supports this, but - user defined types (either classes or structs) are preferred + method that returns a tuple. The language supports this, but + user-defined types (either classes or structs) are preferred for public APIs. - Second, this method is returning the indices to the item in the matrix. + Second, this method returns the indices to the item in the matrix. That leads callers to write code that uses those indices to dereference the matrix and modify a single element: @@ -323,7 +323,7 @@ items: Let's walk through a series of changes to demonstrate the ref local feature and show how to create a method that returns a reference to internal storage. Along the way, you'll learn the rules of the ref return and ref local feature that - protects you from accidentally misusing it. + protect you from accidentally misusing it. Start by modifying the `Find` method declaration so that it returns a `ref int` instead of a tuple. Then, modify the return statement so it returns the value @@ -369,11 +369,11 @@ items: [!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] - Now, the second `WriteLine` statement in the example above will print + Now, the second `WriteLine` statement in the example above prints out the value `24`, indicating that the storage in the matrix has been modified. The local variable has been declared with the `ref` modifier, and it will take a `ref` return. You must initialize a `ref` variable when - it is declared, you cannot split the declaration and the initialization. + it is declared; you cannot split the declaration and the initialization. The C# language has three other rules that protect you from misusing the `ref` locals and returns: @@ -399,7 +399,7 @@ items: Many designs for classes include methods that are called from only one location. These additional private methods keep each method small and focused. However, they can make it harder to understand a class - when reading it the first time. These methods must be understood + when reading it the first time, since these methods are understood outside of the context of the single calling location. For those designs, *local functions* enable you to declare methods @@ -461,9 +461,9 @@ items: > [!NOTE] > Some of the designs that are supported by local functions > could also be accomplished using *lambda expressions*. Those - > interested can [read more about the differences](../../local-functions-vs-lambdas.md) + > interested can [read more about the differences](../../local-functions-vs-lambdas.md). -- title: Throw expressions more concise error reporting +- title: Throw expressions allow more concise error reporting durationInMinutes: 2 content: | In C#, `throw` has always been a statement. Because `throw` is a statement, From dd9df67bdc23eee431320a0f74036eb29061c5f1 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 22 Mar 2019 10:02:30 -0400 Subject: [PATCH 07/18] can't use a : in the content. --- docs/csharp/tutorials/exploration/csharp-7.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index a7d7e5da8a994..9f69418e6e225 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -106,12 +106,12 @@ items: when inspecting the tuple using reflection at runtime, for example. In a tuple assignment, you can also specify the names of the fields - on the right-hand side of the assignment: + on the right-hand side of the assignment: [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] You can specify names for the fields on both the - left and right-hand side of the assignment: + left and right-hand side of the assignment: [!code-csharp[NamedTupleConflict](~/samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] From 8bddbd371f42cda98311d1f1854223beffa47878 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 22 Mar 2019 10:56:50 -0400 Subject: [PATCH 08/18] still experimenting with YML format issues. --- .../csharp/tutorials/exploration/csharp-7.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index 9f69418e6e225..1e85e40648c1b 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -16,22 +16,17 @@ items: - title: Our variable declarations at the assignment location durationInMinutes: 2 content: | - The existing syntax that supports `out` parameters has been improved - in this version. + The existing syntax that supports `out` parameters has been improved in this version. - Previously, you would need to separate the declaration of the out variable - and its initialization into two different statements: + Previously, you would need to separate the declaration of the out variable and its initialization into two different statements: [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#03_OutVariableOldStyle "classic out variable declaration")] - You can now declare `out` variables in the argument list of a method call, - rather than writing a separate declaration statement: + You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement: [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] - You may want to specify the type of the `out` variable for clarity, - as shown above. However, the language does support using an implicitly - typed local variable: + You may want to specify the type of the `out` variable for clarity, as shown above. However, the language does support using an implicitly typed local variable: [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] @@ -40,9 +35,7 @@ items: * No need to assign an initial value. - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. - The most common use for this feature is in the `Try` pattern. In this - pattern, a method returns a `bool` indicating success or failure and an - `out` variable that provides the result if the method succeeds. + The most common use for this feature is in the `Try` pattern. In this pattern, a method returns a `bool` indicating success or failure and an `out` variable that provides the result if the method succeeds. When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards: @@ -58,6 +51,8 @@ items: - title: Tuples create light-weight data structures durationInMinutes: 1 content: | + Put a paragraph before the note in case that's the root cause. + > [!NOTE] > The new tuples features require the types. > You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it From 9bff70389130e883766a489524ff7e713f5ca592 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 22 Mar 2019 11:05:08 -0400 Subject: [PATCH 09/18] experimenting again --- .../csharp/tutorials/exploration/csharp-7.yml | 60 ++++--------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index 1e85e40648c1b..b053c27e282d2 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -54,20 +54,9 @@ items: Put a paragraph before the note in case that's the root cause. > [!NOTE] - > The new tuples features require the types. - > You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it - > on platforms that do not include the types. + > The new tuples features require the types. You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it on platforms that do not include the types. > - > This is similar to other language features that rely on types - > delivered in the framework. Examples include `async` and `await` - > relying on the `INotifyCompletion` interface, and LINQ relying - > on `IEnumerable`. However, the delivery mechanism is changing - > as .NET is becoming more platform independent. The .NET Framework - > may not always ship on the same cadence as the language compiler. When new language - > features rely on new types, those types will be available as NuGet packages when - > the language features ship. As these new types are added to the .NET Standard - > Library and delivered as part of the framework, the NuGet package requirement will - > be removed. + > This is similar to other language features that rely on types delivered in the framework. Examples include `async` and `await` relying on the `INotifyCompletion` interface, and LINQ relying on `IEnumerable`. However, the delivery mechanism is changing as .NET is becoming more platform independent. The .NET Framework may not always ship on the same cadence as the language compiler. When new language features rely on new types, those types will be available as NuGet packages when the language features ship. As these new types are added to the .NET Standard Library and delivered as part of the framework, the NuGet package requirement will be removed. C# provides a rich syntax for classes and structs that explains your design intent. But sometimes that rich syntax requires extra @@ -78,52 +67,31 @@ items: The fields are not validated, and you cannot define your own methods > [!NOTE] - > Tuples were available before C# 7.0, - > but they were inefficient and had no language support. - > This meant that tuple elements could only be referenced as - > `Item1`, `Item2` and so on. C# 7.0 introduces language support for tuples, - > which enables semantic names for the fields of a tuple using new, - > more efficient tuple types. + > Tuples were available before C# 7.0, but they were inefficient and had no language support. This meant that tuple elements could only be referenced as `Item1`, `Item2` and so on. C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new more efficient tuple types. You can create a tuple by assigning a value to each member: [!code-csharp[UnnamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] - That assignment creates a tuple whose members are `Item1` and `Item2`, - which you can use in the same way as - You can change the syntax to create a tuple that provides semantic - names to each of the members of the tuple: + That assignment creates a tuple whose members are `Item1` and `Item2`, which you can use in the same way as You can change the syntax to create a tuple that provides semantic names to each of the members of the tuple: [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] - The `namedLetters` tuple contains fields referred to as `Alpha` and - `Beta`. Those names exist only at compile time and are not preserved -when inspecting the tuple using reflection at runtime, for example. + The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved when inspecting the tuple using reflection at runtime, for example. - In a tuple assignment, you can also specify the names of the fields - on the right-hand side of the assignment: + In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] - You can specify names for the fields on both the - left and right-hand side of the assignment: + You can specify names for the fields on both the left and right-hand side of the assignment: [!code-csharp[NamedTupleConflict](~/samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] - The line above generates a warning, `CS8123`, telling you that the names on the right - side of the assignment, `Alpha` and `Beta`, are ignored because they conflict - with the names on the left side, `First` and `Second`. + The line above generates a warning, `CS8123`, telling you that the names on the right side of the assignment, `Alpha` and `Beta`, are ignored because they conflict with the names on the left side, `First` and `Second`. - The examples above show the basic syntax to declare tuples. Tuples are - most useful as return types for `private` and `internal` methods. Tuples - provide a simple syntax for those methods to return multiple discrete values: - You save the work of authoring a `class` or a `struct` that - defines the type returned. There is no need to create a new type. + The examples above show the basic syntax to declare tuples. Tuples are most useful as return types for `private` and `internal` methods. Tuples provide a simple syntax for those methods to return multiple discrete values: You save the work of authoring a `class` or a `struct` that defines the type returned. There is no need to create a new type. - Creating a tuple is more efficient and more productive. - It has a simpler, lightweight syntax to define a data structure that carries - more than one value. The example method below returns the minimum and maximum - values found in a sequence of integers: + Creating a tuple is more efficient and more productive. It has a simpler, lightweight syntax to define a data structure that carries more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: [!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] @@ -133,15 +101,11 @@ when inspecting the tuple using reflection at runtime, for example. * You do not need to create new type. * The language enhancements removes the need to call the methods. - The declaration for the method provides the names for the fields of the - tuple that is returned. When you call the method, the return value is a - tuple whose fields are `Max` and `Min`: + The declaration for the method provides the names for the fields of the tuple that is returned. When you call the method, the return value is a tuple whose fields are `Max` and `Min`: [!code-csharp[CallingTupleMethod](~/samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] - There may be times when you want to unpackage the members of a tuple that - were returned from a method. You can do that by declaring separate variables - for each of the values in the tuple. This is called *deconstructing* the tuple: + There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This is called *deconstructing* the tuple: [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] From a22936a629f6951e68b1fef840312a655dc61707 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Sun, 24 Mar 2019 16:44:25 -0400 Subject: [PATCH 10/18] Notes and a test build. --- docs/csharp/tutorials/exploration/csharp-7.yml | 17 ++++++++++++++++- docs/csharp/whats-new/csharp-7.md | 16 ++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index b053c27e282d2..fdddbbbca9960 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -24,10 +24,12 @@ items: You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement: + IN OTHER FILE AS WELL [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] You may want to specify the type of the `out` variable for clarity, as shown above. However, the language does support using an implicitly typed local variable: + IN OTHER FILE AS WELL [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] * The code is easier to read. @@ -75,12 +77,14 @@ items: That assignment creates a tuple whose members are `Item1` and `Item2`, which you can use in the same way as You can change the syntax to create a tuple that provides semantic names to each of the members of the tuple: + USED IN OTHER FILE [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved when inspecting the tuple using reflection at runtime, for example. In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: + USED IN OTHER FILE [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] You can specify names for the fields on both the left and right-hand side of the assignment: @@ -107,6 +111,7 @@ items: There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This is called *deconstructing* the tuple: + USED IN OTHER FILE [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] You can also provide a similar deconstruction for any type in .NET. This is @@ -116,10 +121,12 @@ items: this `Point` class that provides a deconstructor method that extracts the `X` and `Y` coordinates: + USED IN OTHER FILE [!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] You can extract the individual fields by assigning a `Point` to a tuple: + USED IN OTHER FILE [!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] You are not bound by the names defined in the `Deconstruct` method. You @@ -147,6 +154,7 @@ items: The following example defines a `QueryCityDataForYears` method that returns a 6-tuple that contains 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. + USED IN OTHER FILE [!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] For more information, see [Discards](../../discards.md). @@ -307,6 +315,7 @@ items: by reference, and helps developers reading the code later remember that the method returns by reference: + USED IN OTHER FILE [!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] Now that the method returns a reference to the integer value in the @@ -326,6 +335,7 @@ items: to the local variable declaration to make the variable a reference when the return value is a reference: + USED IN OTHER FILE [!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] Now, the second `WriteLine` statement in the example above prints @@ -404,6 +414,7 @@ items: `alphabetSubsetImplementation` as a local function inside the public API method: + USED IN OTHER FILE [!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] The version above makes it clear that the local method is referenced @@ -415,6 +426,7 @@ items: exceptions arising from argument validation are thrown before the asynchronous work begins: + USED IN OTHER FILE [!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] > [!NOTE] @@ -475,21 +487,24 @@ items: Binary numbers can get very long, so it's often easier to see the bit patterns by introducing the `_` as a digit separator: + USED IN OTHER FILE [!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] The digit separator can appear anywhere in the constant. For base 10 numbers, it would be common to use it as a thousands separator: + USED IN OTHER FILE [!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] The digit separator can be used with `decimal`, `float` and `double` types as well: + USED IN OTHER FILE [!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] Taken together, you can declare numeric constants with much more readability. - content: | - You've completed an exploration of the new features in C# 6. Now try them yourself in your applications. + You've completed an exploration of the new features in C# 7. Now try them yourself in your applications. diff --git a/docs/csharp/whats-new/csharp-7.md b/docs/csharp/whats-new/csharp-7.md index d4852df0b6554..d08f142ad790b 100644 --- a/docs/csharp/whats-new/csharp-7.md +++ b/docs/csharp/whats-new/csharp-7.md @@ -72,7 +72,7 @@ The fields aren't validated, and you cannot define your own methods You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple: -[!code-csharp[NamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] +[!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and aren't preserved, @@ -81,13 +81,13 @@ for example when inspecting the tuple using reflection at runtime. In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: -[!code-csharp[ImplicitNamedTuple](../../../samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] +[!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This unpackaging is called *deconstructing* the tuple: -[!code-csharp[CallingWithDeconstructor](../../../samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] +[!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] You can also provide a similar deconstruction for any type in .NET. You write a `Deconstruct` method as a member of the class. That `Deconstruct` method provides a set of `out` arguments for each of the @@ -95,11 +95,11 @@ properties you want to extract. Consider this `Point` class that provides a deconstructor method that extracts the `X` and `Y` coordinates: -[!code-csharp[PointWithDeconstruction](../../../samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] +[!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] You can extract the individual fields by assigning a `Point` to a tuple: -[!code-csharp[DeconstructPoint](../../../samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] +[!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] You can learn more in depth about tuples in the [tuples article](../tuples.md). @@ -117,7 +117,7 @@ Discards are supported in the following scenarios: 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. -[!code-csharp[Tuple-discard](../../../samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] +[!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] For more information, see [Discards](../discards.md). @@ -267,7 +267,7 @@ members that can be implemented as expressions. In C# 7.0, you can implement *constructors*, *finalizers*, and `get` and `set` accessors on *properties* and *indexers*. The following code shows examples of each: -[!code-csharp[ExpressionBodiedMembers](../../../samples/snippets/csharp/new-in-7/expressionmembers.cs#36_ExpressionBodiedEverything "new expression-bodied members")] +[!code-csharp[ExpressionBodiedMembers](~/samples/snippets/csharp/new-in-7/expressionmembers.cs#36_ExpressionBodiedEverything "new expression-bodied members")] > [!NOTE] > This example does not need a finalizer, but it is shown @@ -311,7 +311,7 @@ must be accessible. As one concrete example, the `ValueTask` type has been added to the .NET framework to make use of this new language feature: -[!code-csharp[UsingValueTask](../../../samples/snippets/csharp/new-in-7/AsyncWork.cs#30_UsingValueTask "Using ValueTask")] +[!code-csharp[UsingValueTask](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#30_UsingValueTask "Using ValueTask")] > [!NOTE] > You need to add the NuGet package [`System.Threading.Tasks.Extensions`](https://www.nuget.org/packages/System.Threading.Tasks.Extensions/) From 699ad234b97fbe69763bf7d08773738b241e7971 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 27 Mar 2019 13:06:04 -0400 Subject: [PATCH 11/18] rework the interactive tutorial --- .../csharp/tutorials/exploration/csharp-7.yml | 491 +++++------------- docs/csharp/whats-new/csharp-7.md | 26 +- 2 files changed, 135 insertions(+), 382 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index fdddbbbca9960..9687be1972a22 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -11,35 +11,29 @@ metadata: items: - durationInMinutes: 1 content: | - This tutorial lets you explore C# 7 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 6 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. + This tutorial lets you explore C# 7 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 7 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. - title: Our variable declarations at the assignment location durationInMinutes: 2 content: | - The existing syntax that supports `out` parameters has been improved in this version. + The existing syntax that supports `out` parameters has been improved in this version. Try the following code in the interactive window: - Previously, you would need to separate the declaration of the out variable and its initialization into two different statements: + [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableOldStyle "classic out variable declaration")] - [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#03_OutVariableOldStyle "classic out variable declaration")] + You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement. You can move the declaration into the method call: - You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement: + [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableDeclarations "Out variable declarations")] - IN OTHER FILE AS WELL - [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] + You can change the `int` declaration to a `var` declaration: - You may want to specify the type of the `out` variable for clarity, as shown above. However, the language does support using an implicitly typed local variable: - - IN OTHER FILE AS WELL - [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] + [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVarVariableDeclarations "Implicitly typed Out variable")] * The code is easier to read. - You declare the out variable where you use it, not on another line above. * No need to assign an initial value. - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. - The most common use for this feature is in the `Try` pattern. In this pattern, a method returns a `bool` indicating success or failure and an `out` variable that provides the result if the method succeeds. - - When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards: + When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards. Modify the code as shown in the following snippet. ```csharp if (!int.TryParse(input, out int result)) @@ -47,464 +41,223 @@ items: return null; } - return result; + Console.WriteLine(result); ``` - title: Tuples create light-weight data structures durationInMinutes: 1 content: | - Put a paragraph before the note in case that's the root cause. - - > [!NOTE] - > The new tuples features require the types. You must add the NuGet package [`System.ValueTuple`](https://www.nuget.org/packages/System.ValueTuple/) in order to use it on platforms that do not include the types. - > - > This is similar to other language features that rely on types delivered in the framework. Examples include `async` and `await` relying on the `INotifyCompletion` interface, and LINQ relying on `IEnumerable`. However, the delivery mechanism is changing as .NET is becoming more platform independent. The .NET Framework may not always ship on the same cadence as the language compiler. When new language features rely on new types, those types will be available as NuGet packages when the language features ship. As these new types are added to the .NET Standard Library and delivered as part of the framework, the NuGet package requirement will be removed. - - C# provides a rich syntax for classes and structs that explains - your design intent. But sometimes that rich syntax requires extra - work with minimal benefit. You may often write methods that need a simple - structure containing more than one data element. To support these scenarios, - *tuples* were added to C#. Tuples are lightweight data structures - that contain multiple fields to represent the data members. - The fields are not validated, and you cannot define your own methods + Tuples are lightweight data structures that contain multiple fields to represent the data members. The fields are not validated, and you cannot define your own methods. > [!NOTE] > Tuples were available before C# 7.0, but they were inefficient and had no language support. This meant that tuple elements could only be referenced as `Item1`, `Item2` and so on. C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new more efficient tuple types. - You can create a tuple by assigning a value to each member: - - [!code-csharp[UnnamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#04_UnnamedTuple "Unnamed tuple")] + You can create a tuple by assigning a value to each named member: - That assignment creates a tuple whose members are `Item1` and `Item2`, which you can use in the same way as You can change the syntax to create a tuple that provides semantic names to each of the members of the tuple: - - USED IN OTHER FILE - [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] + [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved when inspecting the tuple using reflection at runtime, for example. In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: - USED IN OTHER FILE - [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] - - You can specify names for the fields on both the left and right-hand side of the assignment: - - [!code-csharp[NamedTupleConflict](~/samples/snippets/csharp/new-in-7/program.cs#07_NamedTupleConflict "Named tuple conflict")] + [!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#ImplicitNamedTuple "Implicitly named tuple")] - The line above generates a warning, `CS8123`, telling you that the names on the right side of the assignment, `Alpha` and `Beta`, are ignored because they conflict with the names on the left side, `First` and `Second`. + You can specify names for the fields on both the left and right-hand side of the assignment, but the names on the right side are ignored. - The examples above show the basic syntax to declare tuples. Tuples are most useful as return types for `private` and `internal` methods. Tuples provide a simple syntax for those methods to return multiple discrete values: You save the work of authoring a `class` or a `struct` that defines the type returned. There is no need to create a new type. + Tuples are most useful as return types for `private` and `internal` methods. Tuples provide a simple syntax for those methods to return multiple discrete values. Creating a tuple is more efficient and more productive. It has a simpler, lightweight syntax to define a data structure that carries more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: - [!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#08_TupleReturningMethod "Tuple returning method")] - - Using tuples in this way offers several advantages: - - * You save the work of authoring a `class` or a `struct` that defines the type returned. - * You do not need to create new type. - * The language enhancements removes the need to call the methods. + [!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#TupleReturningMethod "Tuple returning method")] - The declaration for the method provides the names for the fields of the tuple that is returned. When you call the method, the return value is a tuple whose fields are `Max` and `Min`: + There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This is called *deconstructing* the tuple. Add the following code in your browser to try it: - [!code-csharp[CallingTupleMethod](~/samples/snippets/csharp/new-in-7/program.cs#09_CallingTupleMethod "Calling a tuple returning method")] + [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#CallingWithDeconstructor "Deconstructing a tuple")] - There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This is called *deconstructing* the tuple: + As you work with tuples, you'll often find that you don't use sall of the members of a tuple result. When that happens, you can discard one or more of the returned values by using `_` in place of a variable. Add the following code in your browser to try it: - USED IN OTHER FILE - [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] - - You can also provide a similar deconstruction for any type in .NET. This is - done by writing a `Deconstruct` method as a member of the class. That - `Deconstruct` method provides a set of `out` arguments for each of the - properties you want to extract. Consider - this `Point` class that provides a deconstructor method that extracts - the `X` and `Y` coordinates: - - USED IN OTHER FILE - [!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] - - You can extract the individual fields by assigning a `Point` to a tuple: - - USED IN OTHER FILE - [!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] - - You are not bound by the names defined in the `Deconstruct` method. You - can rename the extract variables as part of the assignment: - - [!code-csharp[DeconstructNames](~/samples/snippets/csharp/new-in-7/program.cs#13_DeconstructNames "Deconstruct with new names")] - - You can learn more in depth about tuples in the - [tuples topic](../../tuples.md). - -- title: Use discards to select needed members - durationInMinutes: 2 - content: | - 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, the discard can't be used in code. + [!code-csharp[DiscardTupleMember](~/samples/snippets/csharp/new-in-7/program.cs#DiscardMember "Discard a tuple member")] - Discards are supported in the following scenarios: - - * When deconstructing tuples or user-defined types. - - * When calling methods with [out](../../language-reference/keywords/out-parameter-modifier.md) parameters. - - * In a pattern matching operation with the [is](../../language-reference/keywords/is.md) and [switch](../../language-reference/keywords/switch.md) statements. - - * As a standalone identifier when you want to explicitly identify the value of an assignment as a discard. - - The following example defines a `QueryCityDataForYears` method that returns a 6-tuple that contains 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. - - USED IN OTHER FILE - [!code-csharp[Tuple-discard](~/samples/snippets/csharp/programming-guide/deconstructing-tuples/discard-tuple1.cs)] - - For more information, see [Discards](../../discards.md). + You can learn more in depth about tuples in the [tuples article](../../tuples.md). + You can learn more about discards in the [discards article](../../discards.md). - title: Use the type pattern with the is expression durationInMinutes: 2 content: | - *Pattern matching* is a feature that allows you to implement method dispatch on - properties other than the type of an object. You're probably already familiar - with method dispatch based on the type of an object. In object oriented programming, - virtual and override methods provide language syntax to implement method dispatching - based on an object's type. Base and derived classes provide different implementations. - Pattern matching expressions extend this concept so that you can easily - implement similar dispatch patterns for types and data elements that are - not related through an inheritance hierarchy. - - Pattern matching supports `is` expressions and `switch` expressions. Each - enables inspecting an object and its properties to determine if that object - satisfies the desired pattern. You use the `when` keyword to specify additional - rules to the pattern. - - ### `is` expression - The `is` pattern expression extends the familiar [`is` operator](../../language-reference/keywords/is.md#pattern-matching-with-is) to query an object beyond its type. - Let's start with a simple scenario. We'll add capabilities to this scenario - that demonstrate how pattern matching expressions make algorithms that work - with unrelated types easy. We'll start with a method that computes the sum - of a number of die rolls: + Try the following code in your browser window: - [!code-csharp[SumDieRolls](~/samples/snippets/csharp/new-in-7/patternmatch.cs#14_SumDieRolls "Sum die rolls")] + [!code-csharp[SimpleIs](~/samples/snippets/csharp/new-in-7/patternmatch.cs#SimpleIsPattern "Simple Is pattern")] - You might quickly find that you need to find the sum of die rolls where - some of the rolls are made with multiple dice (dice is the plural of die). Part of the input - sequence may be multiple results instead of a single number: + Change the variable declaration to a string instead: - [!code-csharp[SumDieRollsWithGroups](~/samples/snippets/csharp/new-in-7/patternmatch.cs#15_SumDieRollsWithGroups "Sum die rolls with groups")] + ```csharp + object count = "5"; + ``` + + Now, the `is` expression is false, so the `else` branch is executed. Try to change `count` to `number` in the else branch: - The `is` pattern expression works quite well in this scenario. As part of - checking the type, you write a variable initialization. This creates - a new variable of the validated runtime type. + ```csharp + Console.WriteLine($"{number} is not an integer"); + ``` - As you keep extending these scenarios, you may find that you build more - `if` and `else if` statements. Once that becomes unwieldy, you'll likely - want to switch to `switch` pattern expressions. + The above won't compile because `number` isn't in scope in the `else` branch. It's only in scope in the `true` branch of the `if` statement. + + The `is` expression type pattern is useful when you have a small number of types to test against. Often, you may need to test multiple types. That requires the pattern matching `switch` statement. - title: Pattern matching in the switch statement durationInMinutes: 2 content: | The *match expression* has a familiar syntax, based on the `switch` - statement already part of the C# language. Let's translate the existing code - to use a match expression before adding new cases: + statement already part of the C# language. Let's start with a small sample based on the `is` expression syntax you explored on the previous page: - [!code-csharp[SumUsingSwitch](~/samples/snippets/csharp/new-in-7/patternmatch.cs#16_SumUsingSwitch "Sum using switch")] + [!code-csharp[SimpleSwitch](~/samples/snippets/csharp/new-in-7/patternmatch.cs#SimpleSwitchPattern "simple switch")] - The match expressions have a slightly different syntax than the `is` expressions, where - you declare the type and variable at the beginning of the `case` expression. + The preceding code checks for an `int` or `null`. EVery other type reached the default case. Add the following two lines to verify the behavior: - The match expressions also support constants. This can save time by - factoring out simple cases: + [!code-csharp[AddLongCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestLong "Add a case for long)] - [!code-csharp[SwitchWithConstants](~/samples/snippets/csharp/new-in-7/patternmatch.cs#17_SwitchWithConstants "Switch with constants")] + The `switch` expression will convert a nullable type to its corresponding type. Add the following to verify: - The code above adds cases for `0` as a special case of `int`, and `null` - as a special case when there is no input. This demonstrates one important - new feature in switch pattern expressions: the order of the `case` - expressions now matters. The `0` case must appear before the general `int` - case. Otherwise, the first pattern to match would be the `int` case, - even when the value is `0`. If you accidentally order match expressions such - that a later case has already been handled, the compiler will flag that - and generate an error. + [!code-csharp[NullableCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#NullableSwitch "Add a nullable case")] - This same behavior enables the special case for an empty input sequence. - You can see that the case for an `IEnumerable` item that has elements - must appear before the general `IEnumerable` case. + You can add any number of other type pattern expressions to the switch statements. Add these above the `null` case: - This version has also added a `default` case. The `default` case is always - evaluated last, regardless of the order it appears in the source. For that - reason, convention is to put the `default` case last. + [!code-csharp[MoreTypeCases](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreCases "Add more type cases")] - Finally, let's add one last `case` for a new style of die. Some games - use percentile dice to represent larger ranges of numbers. + Make sure these work by adding the following tests: - > [!NOTE] - > Two 10-sided percentile dice can represent every number from 0 - > through 99. One die has sides labelled `00`, `10`, `20`, ... `90`. The other - > die has sides labeled `0`, `1`, `2`, ... `9`. Add the two die values - > together and you can get every number from 0 through 99. + [!code-csharp[AddMoreTests](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreCases "Add more type tests")] - To add this kind of die to your collection, first define a type to represent - the percentile dice. The `TensDigit` property stores values `0`, `10`, `20`, up to `90`: + The match expressions also support constants. This can save time by factoring out simple cases: - [!code-csharp[18_PercentileDice](~/samples/snippets/csharp/new-in-7/patternmatch.cs#18_PercentileDice "Percentile Die type")] + [!code-csharp[ConstantCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#ConstantCase "Add a constant case")] - Then, add a `case` match expression for the new type: + You must add the preceding case *before* the `case int:` expression. If you add it after that case, the compiler warns you that it has already been handled by a previous case. - [!code-csharp[SwitchWithNewTypes](~/samples/snippets/csharp/new-in-7/patternmatch.cs#19_SwitchWithNewTypes "Include Percentile Die type")] + You can add a `when` clause to any pattern case so that you can test other conditions beyond a type or a constant value. Try it by adding the following case above the general `string` case: - The new syntax for pattern matching expressions makes it easier to create - dispatch algorithms based on an object's type, or other properties, using - a clear and concise syntax. Pattern matching expressions enable these - constructs on data types that are unrelated by inheritance. + [!code-csharp[WhenClause](~/samples/snippets/csharp/new-in-7/patternmatch.cs#WhenClause "Add a when clause")] - You can learn more about pattern matching in the topic - dedicated to [pattern matching in C#](../../pattern-matching.md). + Test it with something like the following code: + + [!code-csharp[TestWhenClause](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestWhenClause "Test the when clause")] + + The new syntax for pattern matching expressions makes it easier to create dispatch algorithms based on an object's type, or other properties, using a clear and concise syntax. Pattern matching expressions enable these constructs on data types that are unrelated by inheritance. + + You can learn more about pattern matching in the article dedicated to [pattern matching in C#](../../pattern-matching.md). - title: Optimize memory storage using ref locals and returns durationInMinutes: 2 content: | - This feature enables algorithms that use and return references - to variables defined elsewhere. One example is working with - large matrices and finding a single location with certain - characteristics. One method would return the two indices for - a single location in the matrix: + This feature enables algorithms that use and return references to variables defined elsewhere. One example is with large matrices and finding a single location with certain characteristics. One method would return the two indices a single location in the matrix: - [!code-csharp[FindReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#20_FindReturningIndices "Find returning indices")] + [!code-csharp[FindReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#EverythingByValue "Find returning indices")] - There are many issues with this code. First of all, it's a public - method that returns a tuple. The language supports this, but - user-defined types (either classes or structs) are preferred - for public APIs. + This method returns the indices to the item in the matrix. That leads callers to write code that uses those indices to dereference the matrix and modify a single element: - Second, this method returns the indices to the item in the matrix. - That leads callers to write code that uses those indices to dereference - the matrix and modify a single element: + [!code-csharp[TestReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#TestByValue "Update Item From Indices")] - [!code-csharp[UpdateItemFromIndices](~/samples/snippets/csharp/new-in-7/program.cs#21_UpdateItemFromIndices "Update Item From Indices")] + You'd rather write a method that returns a *reference* to the element of the matrix that you want to change. - You'd rather write a method that returns a *reference* - to the element of the matrix that you want to change. You could only accomplish - this by using unsafe code and returning a pointer to an `int` in previous versions. + Let's walk through a series of changes to demonstrate the ref local feature and show how to create a method that returns a reference to internal storage. Along the way, you'll learn the rules of the ref return and ref local feature that protect you from accidentally misusing it. - Let's walk through a series of changes to demonstrate the ref local feature - and show how to create a method that returns a reference to internal storage. - Along the way, you'll learn the rules of the ref return and ref local feature that - protect you from accidentally misusing it. + Start by modifying the `Find` method declaration so that it returns a `ref int` instead of a tuple. + + ```csharp + ref int Find(int[,] matrix, Func predicate) + ``` + + Modify the return statement to return the item at the correct indices: - Start by modifying the `Find` method declaration so that it returns a `ref int` - instead of a tuple. Then, modify the return statement so it returns the value - stored in the matrix instead of the two indices: + ```csharp + return matrix[i,j]; + ``` + + Change the final return to throw an exception instead: ```csharp - // Note that this won't compile. - // Method declaration indicates ref return, - // but return statement specifies a value return. - public static ref int Find2(int[,] matrix, Func predicate) - { - for (int i = 0; i < matrix.GetLength(0); i++) - for (int j = 0; j < matrix.GetLength(1); j++) - if (predicate(matrix[i, j])) - return matrix[i, j]; - throw new InvalidOperationException("Not found"); - } + throw new InvalidOperationException("Not found"); ``` - When you declare that a method returns a `ref` variable, you must also - add the `ref` keyword to each return statement. That indicates return - by reference, and helps developers reading the code later remember that - the method returns by reference: + Note that this won't compile. The method declaration indicates a `ref` return, but the return statement specifies a value return. You must add the `ref` keyword to each return statement. That indicates return by reference, and helps developers reading the code later remember that the method returns by reference: - USED IN OTHER FILE - [!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] + ```csharp + return ref matrix[i,j]; + ``` - Now that the method returns a reference to the integer value in the - matrix, you need to modify where it's called. The `var` declaration - means that `valItem` is now an `int` rather than a tuple: + Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple. Change the calling code to following: - [!code-csharp[AssignRefReturnToValue](~/samples/snippets/csharp/new-in-7/program.cs#23_AssignRefReturnToValue "Assign ref return to value")] + ```csharp + var valItem = Find(matrix, (val) => val == 42); + Console.WriteLine(valItem); + valItem = 24; + Console.WriteLine(matrix[4, 2]); + ``` - The second `WriteLine` statement in the example above prints out the value `42`, - not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` - keyword enables the compiler to specify the type, but will not implicitly - add the `ref` modifier. Instead, the value referred to by the `ref return` - is *copied* to the variable on the left-hand side of the assignment. The - variable is not a `ref` local. + The second `WriteLine` statement in the example above prints out the value `42`, not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` keyword enables the compiler to specify the type, but will not implicitly + add the `ref` modifier. Instead, the value referred to by the `ref return` is *copied* to the variable on the left-hand side of the assignment. The variable is not a `ref` local. - In order to get the result you want, you need to add the `ref` modifier - to the local variable declaration to make the variable a reference when - the return value is a reference: + In order to modify the returned reference, you need to add the `ref` modifier to the local variable declaration and before the call to `Find` to make the variable a reference when the return value is a reference. Modify the test code in your browser to match the following: - USED IN OTHER FILE - [!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] + [!code-csharp[TestReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#TestByREf "Update Item From reference")] - Now, the second `WriteLine` statement in the example above prints - out the value `24`, indicating that the storage in the matrix has been - modified. The local variable has been declared with the `ref` modifier, - and it will take a `ref` return. You must initialize a `ref` variable when - it is declared; you cannot split the declaration and the initialization. + Now, the second `WriteLine` statement in the example above prints out the value `24`, indicating that the storage in the matrix has been modified. The local variable has been declared with the `ref` modifier, and it will take a `ref` return. You must initialize a `ref` variable when it is declared; you cannot split the declaration and the initialization. - The C# language has three other rules that protect you from misusing - the `ref` locals and returns: + The C# language has three other rules that protect you from misusing the `ref` locals and returns: * You cannot assign a standard method return value to a `ref` local variable. - - That disallows statements like `ref int i = sequence.Count();` * You cannot return a `ref` to a variable whose lifetime does not extend beyond the execution of the method. - - That means you cannot return a reference to a local variable or a variable with a similar scope. * `ref` locals and returns can't be used with async methods. - - The compiler can't know if the referenced variable has been set to its final value when the async method returns. - - The addition of ref locals and ref returns enable algorithms that are more - efficient by avoiding copying values, or performing dereferencing operations - multiple times. - Adding `ref` to the return value is a [source compatible change](../../whats-new/version-update-considerations.md#source-compatible-changes). Existing code compiles, but the ref return value is copied when assigned. Callers must update the storage for the return value to a `ref` local variable to store the return as a reference. + The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times. For more information, see the [ref keyword](../../language-reference/keywords/ref.md) article. - title: Minimize access to code with local functions durationInMinutes: 2 content: | - Many designs for classes include methods that are called from only - one location. These additional private methods keep each method small - and focused. However, they can make it harder to understand a class - when reading it the first time, since these methods are understood - outside of the context of the single calling location. - - For those designs, *local functions* enable you to declare methods - inside the context of another method. This makes it easier for readers - of the class to see that the local method is only called from the context - in which is it declared. - - There are two very common use cases for local functions: public iterator - methods and public async methods. Both types of methods generate - code that reports errors later than programmers might expect. In - the case of iterator methods, any exceptions are observed only - when calling code that enumerates the returned sequence. In the case - of async methods, any exceptions are only observed when the returned - `Task` is awaited. - - Let's start with an iterator method: - - [!code-csharp[IteratorMethod](~/samples/snippets/csharp/new-in-7/Iterator.cs#25_IteratorMethod "Iterator method")] - - Examine the code below that calls the iterator method incorrectly: - - [!code-csharp[CallIteratorMethod](~/samples/snippets/csharp/new-in-7/program.cs#26_CallIteratorMethod "Call iterator method")] - - The exception is thrown when `resultSet` is iterated, not when `resultSet` is created. - In this contained example, most developers could quickly diagnose the - problem. However, in larger codebases, the code that creates an iterator - often isn't as close to the code that enumerates the result. You can - refactor the code so that the public method validates all arguments, - and a private method generates the enumeration: - - [!code-csharp[IteratorMethodRefactored](~/samples/snippets/csharp/new-in-7/Iterator.cs#27_IteratorMethodRefactored "Iterator method refactored")] - - This refactored version will throw exceptions immediately because the public - method is not an iterator method; only the private method uses the - `yield return` syntax. However, there are potential problems with this - refactoring. The private method should only be called from the public - interface method, because otherwise all argument validation is skipped. - Readers of the class must discover this fact by reading the entire class - and searching for any other references to the `alphabetSubsetImplementation` - method. - - You can make that design intent more clear by declaring the - `alphabetSubsetImplementation` as a local function inside the public - API method: - - USED IN OTHER FILE - [!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] - - The version above makes it clear that the local method is referenced - only in the context of the outer method. The rules for local functions - also ensure that a developer can't accidentally call the local function - from another location in the class and bypass the argument validation. - - The same technique can be employed with `async` methods to ensure that - exceptions arising from argument validation are thrown before the asynchronous - work begins: - - USED IN OTHER FILE - [!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] - - > [!NOTE] - > Some of the designs that are supported by local functions - > could also be accomplished using *lambda expressions*. Those - > interested can [read more about the differences](../../local-functions-vs-lambdas.md). - -- title: Throw expressions allow more concise error reporting - durationInMinutes: 2 - content: | - In C#, `throw` has always been a statement. Because `throw` is a statement, - not an expression, there were C# constructs where you could not use it. These - included conditional expressions, null coalescing expressions, and some lambda - expressions. The addition of expression-bodied members adds more locations - where `throw` expressions would be useful. So that you can write any of these - constructs, C# 7.0 introduces *throw expressions*. - - The syntax is the same as you've always used for `throw` statements. The only difference - is that now you can place them in new locations, such as in a conditional expression: - - [!code-csharp[Throw_ExpressionExample](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#37_Throw_ExpressionExample "conditional throw expressions")] - - This features enables using throw expressions in initialization expressions: + You can now declare local functions that are nested inside other functions. This enables you to minimize the visibility of these functions. There are three obvious use cases for local functions: - [!code-csharp[ThrowInInitialization](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#38_ThrowInInitialization "conditional throw expressions")] + - Recursive functions. + - Iterator methods. + - Asynchronous methods. - Previously, those initializations would need to be in a constructor, with the - throw statements in the body of the constructor: + Let's start with recursive methods. Try the following code in the browser to calculate `6!` (factorial): + ```csharp + int LocalFunctionFactorial(int n) + { + return nthFactorial(n); - [!code-csharp[ThrowInConstructor](~/samples/snippets/csharp/new-in-7/throwexpressions.cs#39_ThrowInConstructor "throw statements")] - - > [!NOTE] - > Both of the preceding constructs will cause exceptions to be thrown during - > the construction of an object. Those are often difficult to recover from. - > For that reason, designs that throw exceptions during construction are - > discouraged. - -- title: More readable numeric literals - durationInMinutes: 2 - content: | - Misreading numeric constants can make it harder to understand - code when reading it for the first time. This often - occurs when those numbers are used as bit masks or other symbolic - rather than numeric values. C# 7.0 includes two new features to - make it easier to write numbers in the most readable fashion - for the intended use: *binary literals*, and *digit separators*. - - For those times when you are creating bit masks, or whenever a - binary representation of a number makes the most readable code, - write that number in binary: + int nthFactorial(int number) => (number < 2) ? + 1 : number * nthFactorial(number - 1); + } - [!code-csharp[BinaryConstants](~/samples/snippets/csharp/new-in-7/Program.cs#32_BinaryConstants "Binary constants")] + Console.WriteLine(LocalFunctionFactorial(6)); + ``` - The `0b` at the beginning of the constant indicates that the - number is written as a binary number. + Local functions are a great way to implement recursive algorithms. Other common uses are for public iterator methods and public async methods. Both types of methods generate code that reports errors later than programmers might expect. In the case of iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. In the case of async methods, any exceptions are only observed when the returned `Task` is awaited. - Binary numbers can get very long, so it's often easier to see - the bit patterns by introducing the `_` as a digit separator: + Iterator methods are easier to explore in the browser, so let's use those in this exploration. Try the following code that calls an iterator method in your browser: - USED IN OTHER FILE - [!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] + [!code-csharp[IteratorMethod](~/samples/snippets/csharp/new-in-7/Iterator.cs#SnippetIteratorMethod "Iterator method")] - The digit separator can appear anywhere in the constant. For base 10 - numbers, it would be common to use it as a thousands separator: + Run the code. Notice that the exception is thrown when the code begins iterating the second result set. The code that iterates the first result set has already run. This sample is both small and doesn't change any data structures, dso it's harmless and easy to fix. But, in a larger program, where the two iterator objects may be created in different child methods the root cause could be hard to find. If the first iterator method changed data state, it could even cause data corruption. You'd prefer the exception was thrown immediately, before any work is done. You can refactor the code so that the public method validates all arguments, and a local function that performs the enumeration: - USED IN OTHER FILE - [!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] + [!code-csharp[IteratorMethodRefactored](~/samples/snippets/csharp/new-in-7/Iterator.cs#IteratorMethodLocalInteractive "Iterator method refactored")] - The digit separator can be used with `decimal`, `float` and `double` - types as well: + The preceding version makes it clear that the local method is referenced only in the context of the outer method. The rules for local functions also ensure that a developer can't accidentally call the local function from another location in the class and bypass the argument validation. - USED IN OTHER FILE - [!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] + The same technique can be employed with `async` methods to ensure that exceptions arising from argument validation are thrown before the asynchronous + work begins. - Taken together, you can declare numeric constants with much more - readability. + > [!NOTE] + > Some of the designs that are supported by local functions + > could also be accomplished using *lambda expressions*. Those + > interested can [read more about the differences](../../local-functions-vs-lambdas.md). - content: | - You've completed an exploration of the new features in C# 7. Now try them yourself in your applications. + You've completed an exploration of the major new features in C# 7. Now try them yourself in your applications. You can see the full list in the [what's new in C# 7](../../whats-new/cshar-7.md) article. diff --git a/docs/csharp/whats-new/csharp-7.md b/docs/csharp/whats-new/csharp-7.md index d08f142ad790b..77ea8b7e3e3b6 100644 --- a/docs/csharp/whats-new/csharp-7.md +++ b/docs/csharp/whats-new/csharp-7.md @@ -38,13 +38,13 @@ The existing syntax that supports `out` parameters has been improved in this version. You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement: -[!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#01_OutVariableDeclarations "Out variable declarations")] +[!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableDeclarations "Out variable declarations")] You may want to specify the type of the `out` variable for clarity, as shown above. However, the language does support using an implicitly typed local variable: -[!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#02_OutVarVariableDeclarations "Implicitly typed Out variable")] +[!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVarVariableDeclarations "Implicitly typed Out variable")] * The code is easier to read. - You declare the out variable where you use it, not on another line above. @@ -72,7 +72,7 @@ The fields aren't validated, and you cannot define your own methods You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple: -[!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#05_NamedTuple "Named tuple")] +[!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#NamedTuple "Named tuple")] The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and aren't preserved, @@ -81,13 +81,13 @@ for example when inspecting the tuple using reflection at runtime. In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: -[!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#06_ImplicitNamedTuple "Implicitly named tuple")] +[!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#ImplicitNamedTuple "Implicitly named tuple")] There may be times when you want to unpackage the members of a tuple that were returned from a method. You can do that by declaring separate variables for each of the values in the tuple. This unpackaging is called *deconstructing* the tuple: -[!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#10_CallingWithDeconstructor "Deconstructing a tuple")] +[!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#CallingWithDeconstructor "Deconstructing a tuple")] You can also provide a similar deconstruction for any type in .NET. You write a `Deconstruct` method as a member of the class. That `Deconstruct` method provides a set of `out` arguments for each of the @@ -95,11 +95,11 @@ properties you want to extract. Consider this `Point` class that provides a deconstructor method that extracts the `X` and `Y` coordinates: -[!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#11_PointWithDeconstruction "Point with deconstruction method")] +[!code-csharp[PointWithDeconstruction](~/samples/snippets/csharp/new-in-7/point.cs#PointWithDeconstruction "Point with deconstruction method")] You can extract the individual fields by assigning a `Point` to a tuple: -[!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#12_DeconstructPoint "Deconstruct a point")] +[!code-csharp[DeconstructPoint](~/samples/snippets/csharp/new-in-7/program.cs#DeconstructPoint "Deconstruct a point")] You can learn more in depth about tuples in the [tuples article](../tuples.md). @@ -246,13 +246,13 @@ when calling code that enumerates the returned sequence. In async methods, any exceptions are only observed when the returned `Task` is awaited. The following example demonstrates separating parameter validation from the iterator implementation using a local function: -[!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#28_IteratorMethodLocal "Iterator method with local function")] +[!code-csharp[22_IteratorMethodLocal](~/samples/snippets/csharp/new-in-7/Iterator.cs#IteratorMethodLocal "Iterator method with local function")] The same technique can be employed with `async` methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins: -[!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#29_TaskExample "Task returning method with local function")] +[!code-csharp[TaskExample](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#TaskExample "Task returning method with local function")] > [!NOTE] > Some of the designs that are supported by local functions @@ -267,7 +267,7 @@ members that can be implemented as expressions. In C# 7.0, you can implement *constructors*, *finalizers*, and `get` and `set` accessors on *properties* and *indexers*. The following code shows examples of each: -[!code-csharp[ExpressionBodiedMembers](~/samples/snippets/csharp/new-in-7/expressionmembers.cs#36_ExpressionBodiedEverything "new expression-bodied members")] +[!code-csharp[ExpressionBodiedMembers](~/samples/snippets/csharp/new-in-7/expressionmembers.cs#ExpressionBodiedEverything "new expression-bodied members")] > [!NOTE] > This example does not need a finalizer, but it is shown @@ -311,7 +311,7 @@ must be accessible. As one concrete example, the `ValueTask` type has been added to the .NET framework to make use of this new language feature: -[!code-csharp[UsingValueTask](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#30_UsingValueTask "Using ValueTask")] +[!code-csharp[UsingValueTask](~/samples/snippets/csharp/new-in-7/AsyncWork.cs#UsingValueTask "Using ValueTask")] > [!NOTE] > You need to add the NuGet package [`System.Threading.Tasks.Extensions`](https://www.nuget.org/packages/System.Threading.Tasks.Extensions/) @@ -337,12 +337,12 @@ number is written as a binary number. Binary numbers can get long, so it's often the bit patterns by introducing the `_` as a digit separator, as shown above in the binary constant. The digit separator can appear anywhere in the constant. For base 10 numbers, it would be common to use it as a thousands separator: -[!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#34_LargeIntegers "Large integer")] +[!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#LargeIntegers "Large integer")] The digit separator can be used with `decimal`, `float`, and `double` types as well: -[!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#35_OtherConstants "non-integral constants")] +[!code-csharp[OtherConstants](~/samples/snippets/csharp/new-in-7/Program.cs#OtherConstants "non-integral constants")] Taken together, you can declare numeric constants with much more readability. From 7099e6b2b7b19c0b4c142c12bc9d1b256321ba18 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 27 Mar 2019 14:08:43 -0400 Subject: [PATCH 12/18] typo / fix broken link --- docs/csharp/tutorials/exploration/csharp-7.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index 9687be1972a22..d28739dbf18b1 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -259,5 +259,5 @@ items: > interested can [read more about the differences](../../local-functions-vs-lambdas.md). - content: | - You've completed an exploration of the major new features in C# 7. Now try them yourself in your applications. You can see the full list in the [what's new in C# 7](../../whats-new/cshar-7.md) article. + You've completed an exploration of the major new features in C# 7. Now try them yourself in your applications. You can see the full list in the [what's new in C# 7](../../whats-new/csharp-7.md) article. From 34843894ceff7f6ab9cf2a58b5cd2cf169094921 Mon Sep 17 00:00:00 2001 From: Petr Kulikov Date: Wed, 27 Mar 2019 15:48:43 -0400 Subject: [PATCH 13/18] Apply suggestions from code review Co-Authored-By: BillWagner --- docs/csharp/tutorials/exploration/csharp-7.yml | 10 +++++----- docs/toc.yml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index d28739dbf18b1..43820ae21f75c 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -1,8 +1,8 @@ ### YamlMime:Tutorial -title: Explore C# 7 - C# interactive C# tutorial +title: Explore C# 7.0 - C# interactive tutorial metadata: - title: Explore C# 7 - Try the new features in C# 6 interactively, using your browser - description: In this tutorial, you'll use your browser to explore C# 6 interactively. You'll explore the new idioms you can use with C# 6 that enable more concise and readable code. + title: Explore C# 7.0 - Try the new features in C# 7.0 interactively, using your browser + description: In this tutorial, you'll use your browser to explore C# 7.0 interactively. You'll explore the new idioms you can use with C# 7.0 that enable more concise and readable code. audience: Developer level: intermediate ms.date: 03/20/2019 @@ -11,7 +11,7 @@ metadata: items: - durationInMinutes: 1 content: | - This tutorial lets you explore C# 7 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 7 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. + This tutorial lets you explore C# 7.0 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 7.0 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. - title: Our variable declarations at the assignment location durationInMinutes: 2 @@ -187,7 +187,7 @@ items: return ref matrix[i,j]; ``` - Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple. Change the calling code to following: + Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple. Change the calling code to following: ```csharp var valItem = Find(matrix, (val) => val == 42); diff --git a/docs/toc.yml b/docs/toc.yml index 1bb0b053ca7e2..8acb783ecde2c 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -527,7 +527,7 @@ href: csharp/tutorials/intro-to-csharp/introduction-to-classes.md - name: Explore C# 6 href: csharp/tutorials/exploration/csharp-6.yml - - name: Explore C# 7 + - name: Explore C# 7.0 href: csharp/tutorials/exploration/csharp-7.yml - name: Work with nullable reference types href: csharp/tutorials/nullable-reference-types.md @@ -2450,4 +2450,4 @@ - name: Data transforms href: machine-learning/resources/transforms.md - name: Samples and Tutorials - href: samples-and-tutorials/index.md \ No newline at end of file + href: samples-and-tutorials/index.md From 19c84ef5997ad307bee8bc6be1ee04c6793a857b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 29 Mar 2019 09:53:47 -0400 Subject: [PATCH 14/18] Fix the samples for ref These don't work unless the sample in the browser is a full program. Ref returns in snippets don't compile in the current scaffolding. --- .../csharp/tutorials/exploration/csharp-7.yml | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index 43820ae21f75c..eab74a17c1058 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -20,11 +20,11 @@ items: [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableOldStyle "classic out variable declaration")] - You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement. You can move the declaration into the method call: + You can now declare `out` variables in the argument list of a method call, rather than writing a separate declaration statement. You can move the declaration into the method call. Add the following code to the bottom of the interactive window: [!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableDeclarations "Out variable declarations")] - You can change the `int` declaration to a `var` declaration: + You can change the `int` declaration to a `var` declaration. Add the following code to the interactive window: [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVarVariableDeclarations "Implicitly typed Out variable")] @@ -33,7 +33,7 @@ items: * No need to assign an initial value. - By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned. - When using the `out` variable declaration, the declared variable "leaks" into the outer scope of the if statement. This allows you to use the variable afterwards. Modify the code as shown in the following snippet. + The declared variable's scope is the scope enclosing the `if` statement. This allows you to use the variable afterwards. Modify the last `if` block as shown in the following snippet. ```csharp if (!int.TryParse(input, out int result)) @@ -102,7 +102,7 @@ items: Console.WriteLine($"{number} is not an integer"); ``` - The above won't compile because `number` isn't in scope in the `else` branch. It's only in scope in the `true` branch of the `if` statement. + The above won't compile because `number` isn't assigned in the `else` branch. It's only assigned in the `true` branch of the `if` statement. The `is` expression type pattern is useful when you have a small number of types to test against. Often, you may need to test multiple types. That requires the pattern matching `switch` statement. @@ -128,7 +128,7 @@ items: Make sure these work by adding the following tests: - [!code-csharp[AddMoreTests](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreCases "Add more type tests")] + [!code-csharp[AddMoreTests](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreTests "Add more type tests")] The match expressions also support constants. This can save time by factoring out simple cases: @@ -153,20 +153,42 @@ items: content: | This feature enables algorithms that use and return references to variables defined elsewhere. One example is with large matrices and finding a single location with certain characteristics. One method would return the two indices a single location in the matrix: - [!code-csharp[FindReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#EverythingByValue "Find returning indices")] - - This method returns the indices to the item in the matrix. That leads callers to write code that uses those indices to dereference the matrix and modify a single element: + ```csharp + public class Program + { + private static (int i, int j) Find(int[,] matrix, Func predicate) + { + for (int i = 0; i < matrix.GetLength(0); i++) + for (int j = 0; j < matrix.GetLength(1); j++) + if (predicate(matrix[i, j])) + return (i, j); + return (-1, -1); // Not found + } + + public static void Main() + { + int[,] sourceMatrix = new int[10, 10]; + for (int x = 0; x < 10; x++) + for (int y = 0; y < 10; y++) + sourceMatrix[x, y] = x * 10 + y; + + var indices = Find(sourceMatrix, (val) => val == 42); + Console.WriteLine(indices); + sourceMatrix[indices.i, indices.j] = 24; + } - [!code-csharp[TestReturningIndices](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#TestByValue "Update Item From Indices")] + } + ``` - You'd rather write a method that returns a *reference* to the element of the matrix that you want to change. + + This `Find` method returns the indices to the item in the matrix. That leads callers to write code that uses those indices to dereference the matrix and modify a single element. You'd rather write a method that returns a *reference* to the element of the matrix that you want to change. Let's walk through a series of changes to demonstrate the ref local feature and show how to create a method that returns a reference to internal storage. Along the way, you'll learn the rules of the ref return and ref local feature that protect you from accidentally misusing it. Start by modifying the `Find` method declaration so that it returns a `ref int` instead of a tuple. ```csharp - ref int Find(int[,] matrix, Func predicate) + private static ref int Find(int[,] matrix, Func predicate) ``` Modify the return statement to return the item at the correct indices: @@ -187,7 +209,7 @@ items: return ref matrix[i,j]; ``` - Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple. Change the calling code to following: + Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The `var` declaration means that `valItem` is now an `int` rather than a tuple. Change the calling code in the `Main` method to the following: ```csharp var valItem = Find(matrix, (val) => val == 42); @@ -199,9 +221,22 @@ items: The second `WriteLine` statement in the example above prints out the value `42`, not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` keyword enables the compiler to specify the type, but will not implicitly add the `ref` modifier. Instead, the value referred to by the `ref return` is *copied* to the variable on the left-hand side of the assignment. The variable is not a `ref` local. - In order to modify the returned reference, you need to add the `ref` modifier to the local variable declaration and before the call to `Find` to make the variable a reference when the return value is a reference. Modify the test code in your browser to match the following: + In order to modify the returned reference, you need to add the `ref` modifier to the local variable declaration and before the call to `Find` to make the variable a reference when the return value is a reference. Modify the `Main` method in your browser to match the following: - [!code-csharp[TestReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#TestByREf "Update Item From reference")] + ```csharp + public static void Main() + { + int[,] sourceMatrix = new int[10, 10]; + for (int x = 0; x < 10; x++) + for (int y = 0; y < 10; y++) + sourceMatrix[x, y] = x * 10 + y; + + ref var item = ref Find(sourceMatrix, (val) => val == 42); + Console.WriteLine(item); + item = 24; + Console.WriteLine(sourceMatrix[4, 2]); + } + ``` Now, the second `WriteLine` statement in the example above prints out the value `24`, indicating that the storage in the matrix has been modified. The local variable has been declared with the `ref` modifier, and it will take a `ref` return. You must initialize a `ref` variable when it is declared; you cannot split the declaration and the initialization. From 58eabc51b166fb7d6e8d08f737df357739e1049d Mon Sep 17 00:00:00 2001 From: Ron Petrusha Date: Fri, 29 Mar 2019 14:33:14 -0400 Subject: [PATCH 15/18] Apply suggestions from code review Co-Authored-By: BillWagner --- docs/csharp/tutorials/exploration/csharp-7.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index eab74a17c1058..ed90f4c7ebea3 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -13,7 +13,7 @@ items: content: | This tutorial lets you explore C# 7.0 features interactively, using your browser to write C# and see the results of compiling and running your code. It contains a series of lessons that modify earlier C# practices to use newer, more concise C# 7.0 features. The rest of this article provides an overview of each of these features, with a link to explore each feature. -- title: Our variable declarations at the assignment location +- title: Out variable declarations at the assignment location durationInMinutes: 2 content: | The existing syntax that supports `out` parameters has been improved in this version. Try the following code in the interactive window: @@ -56,7 +56,7 @@ items: [!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#NamedTuple "Named tuple")] - The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved when inspecting the tuple using reflection at runtime, for example. + The `namedLetters` tuple contains fields referred to as `Alpha` and `Beta`. Those names exist only at compile time and are not preserved at runtime (when inspecting the tuple using reflection, for example). In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment: @@ -74,7 +74,7 @@ items: [!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#CallingWithDeconstructor "Deconstructing a tuple")] - As you work with tuples, you'll often find that you don't use sall of the members of a tuple result. When that happens, you can discard one or more of the returned values by using `_` in place of a variable. Add the following code in your browser to try it: + As you work with tuples, you'll often find that you don't use all of the members of a tuple result. When that happens, you can discard one or more of the returned values by using `_` in place of a variable. Add the following code in your browser to try it: [!code-csharp[DiscardTupleMember](~/samples/snippets/csharp/new-in-7/program.cs#DiscardMember "Discard a tuple member")] @@ -114,15 +114,15 @@ items: [!code-csharp[SimpleSwitch](~/samples/snippets/csharp/new-in-7/patternmatch.cs#SimpleSwitchPattern "simple switch")] - The preceding code checks for an `int` or `null`. EVery other type reached the default case. Add the following two lines to verify the behavior: + The preceding code checks for an `int` or `null`. Every other type reached the default case. Add the following two lines to verify the behavior: - [!code-csharp[AddLongCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestLong "Add a case for long)] + [!code-csharp[AddLongCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestLong "Add a case for long")] The `switch` expression will convert a nullable type to its corresponding type. Add the following to verify: [!code-csharp[NullableCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#NullableSwitch "Add a nullable case")] - You can add any number of other type pattern expressions to the switch statements. Add these above the `null` case: + You can add any number of other type pattern expressions to the switch statements. Add these before the `null` case: [!code-csharp[MoreTypeCases](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreCases "Add more type cases")] @@ -144,7 +144,7 @@ items: [!code-csharp[TestWhenClause](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestWhenClause "Test the when clause")] - The new syntax for pattern matching expressions makes it easier to create dispatch algorithms based on an object's type, or other properties, using a clear and concise syntax. Pattern matching expressions enable these constructs on data types that are unrelated by inheritance. + The new syntax for pattern matching expressions makes it easier to create dispatch algorithms using a clear and concise syntax based on an object's type or other properties. Pattern matching expressions enable these constructs on data types that are unrelated by inheritance. You can learn more about pattern matching in the article dedicated to [pattern matching in C#](../../pattern-matching.md). @@ -218,7 +218,7 @@ items: Console.WriteLine(matrix[4, 2]); ``` - The second `WriteLine` statement in the example above prints out the value `42`, not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` keyword enables the compiler to specify the type, but will not implicitly + The second `WriteLine` statement in the example above prints out the value `42`, not `24`. The variable `valItem` is an `int`, not a `ref int`. The `var` keyword enables the compiler to specify the type but will not implicitly add the `ref` modifier. Instead, the value referred to by the `ref return` is *copied* to the variable on the left-hand side of the assignment. The variable is not a `ref` local. In order to modify the returned reference, you need to add the `ref` modifier to the local variable declaration and before the call to `Find` to make the variable a reference when the return value is a reference. Modify the `Main` method in your browser to match the following: @@ -246,7 +246,7 @@ items: * You cannot return a `ref` to a variable whose lifetime does not extend beyond the execution of the method. * `ref` locals and returns can't be used with async methods. - The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times. + The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values or performing dereferencing operations multiple times. For more information, see the [ref keyword](../../language-reference/keywords/ref.md) article. From 282e14e4a6681b5ab43e22b291254b569654eef3 Mon Sep 17 00:00:00 2001 From: Ron Petrusha Date: Fri, 29 Mar 2019 14:39:52 -0400 Subject: [PATCH 16/18] Apply suggestions from code review Co-Authored-By: BillWagner --- docs/csharp/whats-new/csharp-7.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/csharp/whats-new/csharp-7.md b/docs/csharp/whats-new/csharp-7.md index 77ea8b7e3e3b6..6c5b2b3dcd3b5 100644 --- a/docs/csharp/whats-new/csharp-7.md +++ b/docs/csharp/whats-new/csharp-7.md @@ -59,7 +59,7 @@ work with minimal benefit. You may often write methods that need a simple structure containing more than one data element. To support these scenarios *tuples* were added to C#. Tuples are lightweight data structures that contain multiple fields to represent the data members. -The fields aren't validated, and you cannot define your own methods +The fields aren't validated, and you can't define your own methods > [!NOTE] > Tuples were available before C# 7.0, @@ -125,7 +125,7 @@ For more information, see [Discards](../discards.md). *Pattern matching* is a feature that allows you to implement method dispatch on properties other than the type of an object. You're probably already familiar -with method dispatch based on the type of an object. In Object-Oriented programming, +with method dispatch based on the type of an object. In object-oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Base and Derived classes provide different implementations. Pattern matching expressions extend this concept so that you can easily @@ -191,8 +191,8 @@ public static int SumPositiveNumbers(IEnumerable sequence) - `case null:` is the null pattern. - `default:` is the familiar default case. -You can learn more about pattern matching in the article -dedicated to [pattern matching in C#](../pattern-matching.md). +You can learn more about pattern matching in +[Pattern Matching in C#](../pattern-matching.md). ## Ref locals and returns @@ -201,16 +201,16 @@ to variables defined elsewhere. One example is working with large matrices, and finding a single location with certain characteristics. The following method returns a **reference** to that storage in the matrix: -[!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#22_FindReturningRef "Find returning by reference")] +[!code-csharp[FindReturningRef](~/samples/snippets/csharp/new-in-7/MatrixSearch.cs#FindReturningRef "Find returning by reference")] You can declare the return value as a `ref` and modify that value in the matrix, as shown in the following code: -[!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#24_AssignRefReturn "Assign ref return")] +[!code-csharp[AssignRefReturn](~/samples/snippets/csharp/new-in-7/program.cs#AssignRefReturn "Assign ref return")] The C# language has several rules that protect you from misusing the `ref` locals and returns: -* You must add the `ref` keyword to the method signature, and to all `return` statements in a method. +* You must add the `ref` keyword to the method signature and to all `return` statements in a method. - That makes it clear the method returns by reference throughout the method. * A `ref return` may be assigned to a value variable, or a `ref` variable. - The return value is copied into a variable, a `ref` variable refers to the same storage. @@ -330,12 +330,12 @@ For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary: -[!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#33_ThousandSeparators "Thousands separators")] +[!code-csharp[ThousandSeparators](~/samples/snippets/csharp/new-in-7/Program.cs#ThousandSeparators "Thousands separators")] The `0b` at the beginning of the constant indicates that the number is written as a binary number. Binary numbers can get long, so it's often easier to see the bit patterns by introducing the `_` as a digit separator, as shown above in the binary constant. The digit separator can appear anywhere in the constant. For base 10 -numbers, it would be common to use it as a thousands separator: +numbers, it is common to use it as a thousands separator: [!code-csharp[LargeIntegers](~/samples/snippets/csharp/new-in-7/Program.cs#LargeIntegers "Large integer")] From aae6797dd8b8c041e06a77e738eaefb1bf668a19 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 29 Mar 2019 14:47:17 -0400 Subject: [PATCH 17/18] respond to final feedback --- docs/csharp/tutorials/exploration/csharp-7.yml | 6 ++++-- docs/csharp/whats-new/csharp-7.md | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index ed90f4c7ebea3..e1db34b97e559 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -16,7 +16,7 @@ items: - title: Out variable declarations at the assignment location durationInMinutes: 2 content: | - The existing syntax that supports `out` parameters has been improved in this version. Try the following code in the interactive window: + The existing syntax that supports `out` parameters has been improved in this version. Click the *Enter Focus Mode* button at the bottom of this page, then try the following code in the interactive window: [!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableOldStyle "classic out variable declaration")] @@ -28,6 +28,8 @@ items: [!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVarVariableDeclarations "Implicitly typed Out variable")] + The new syntax provides two important advantages over the existing syntax: + * The code is easier to read. - You declare the out variable where you use it, not on another line above. * No need to assign an initial value. @@ -66,7 +68,7 @@ items: Tuples are most useful as return types for `private` and `internal` methods. Tuples provide a simple syntax for those methods to return multiple discrete values. - Creating a tuple is more efficient and more productive. It has a simpler, lightweight syntax to define a data structure that carries more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: + Creating a tuple is more efficient and more productive that creating a class or struct. It has a simpler, lightweight syntax to define a data structure that carries more than one value. The example method below returns the minimum and maximum values found in a sequence of integers: [!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#TupleReturningMethod "Tuple returning method")] diff --git a/docs/csharp/whats-new/csharp-7.md b/docs/csharp/whats-new/csharp-7.md index 6c5b2b3dcd3b5..6b44fa99b28e4 100644 --- a/docs/csharp/whats-new/csharp-7.md +++ b/docs/csharp/whats-new/csharp-7.md @@ -213,7 +213,7 @@ the `ref` locals and returns: * You must add the `ref` keyword to the method signature and to all `return` statements in a method. - That makes it clear the method returns by reference throughout the method. * A `ref return` may be assigned to a value variable, or a `ref` variable. - - The return value is copied into a variable, a `ref` variable refers to the same storage. + - The caller controls whether the return value is copied or not. Omitting the `ref` modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage. * You can't assign a standard method return value to a `ref` local variable. - That disallows statements like `ref int i = sequence.Count();` * You can't return a `ref` to a variable whose lifetime doesn't extend beyond the execution of the method. From 9b0c1db814bc6fe2ec8fea3656442e4acb4d5f5f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 29 Mar 2019 15:14:28 -0400 Subject: [PATCH 18/18] add note about try.net compiler issue --- docs/csharp/tutorials/exploration/csharp-7.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/csharp/tutorials/exploration/csharp-7.yml b/docs/csharp/tutorials/exploration/csharp-7.yml index e1db34b97e559..685529a4554c4 100644 --- a/docs/csharp/tutorials/exploration/csharp-7.yml +++ b/docs/csharp/tutorials/exploration/csharp-7.yml @@ -106,6 +106,9 @@ items: The above won't compile because `number` isn't assigned in the `else` branch. It's only assigned in the `true` branch of the `if` statement. + > [!NOTE] + > There is an [issue](https://github.com/dotnet/try/issues/175) where you may get incorrect output in the preceding example. + The `is` expression type pattern is useful when you have a small number of types to test against. Often, you may need to test multiple types. That requires the pattern matching `switch` statement. - title: Pattern matching in the switch statement