Skip to content

Commit b6f21eb

Browse files
authored
Make new 7 interactive (#11442)
* finish trimming "what's new" * add interactive tutorial * fix the build errors * build errors, round 2 * one more build warning * Apply suggestions from code review A very thorough early review from Ron Co-Authored-By: BillWagner <wiwagn@microsoft.com> * can't use a : in the content. * still experimenting with YML format issues. * experimenting again * Notes and a test build. * rework the interactive tutorial * typo / fix broken link * Apply suggestions from code review Co-Authored-By: BillWagner <wiwagn@microsoft.com> * 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. * Apply suggestions from code review Co-Authored-By: BillWagner <wiwagn@microsoft.com> * Apply suggestions from code review Co-Authored-By: BillWagner <wiwagn@microsoft.com> * respond to final feedback * add note about try.net compiler issue
1 parent 02331f3 commit b6f21eb

File tree

4 files changed

+419
-372
lines changed

4 files changed

+419
-372
lines changed
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
### YamlMime:Tutorial
2+
title: Explore C# 7.0 - C# interactive tutorial
3+
metadata:
4+
title: Explore C# 7.0 - Try the new features in C# 7.0 interactively, using your browser
5+
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.
6+
audience: Developer
7+
level: intermediate
8+
ms.date: 03/20/2019
9+
displayType: two-column
10+
interactive: csharp
11+
items:
12+
- durationInMinutes: 1
13+
content: |
14+
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.
15+
16+
- title: Out variable declarations at the assignment location
17+
durationInMinutes: 2
18+
content: |
19+
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:
20+
21+
[!code-csharp[OutVariableOldStyle](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableOldStyle "classic out variable declaration")]
22+
23+
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:
24+
25+
[!code-csharp[OutVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVariableDeclarations "Out variable declarations")]
26+
27+
You can change the `int` declaration to a `var` declaration. Add the following code to the interactive window:
28+
29+
[!code-csharp[OutVarVariableDeclarations](~/samples/snippets/csharp/new-in-7/program.cs#OutVarVariableDeclarations "Implicitly typed Out variable")]
30+
31+
The new syntax provides two important advantages over the existing syntax:
32+
33+
* The code is easier to read.
34+
- You declare the out variable where you use it, not on another line above.
35+
* No need to assign an initial value.
36+
- By declaring the `out` variable where it is used in a method call, you can't accidentally use it before it is assigned.
37+
38+
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.
39+
40+
```csharp
41+
if (!int.TryParse(input, out int result))
42+
{
43+
return null;
44+
}
45+
46+
Console.WriteLine(result);
47+
```
48+
49+
- title: Tuples create light-weight data structures
50+
durationInMinutes: 1
51+
content: |
52+
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.
53+
54+
> [!NOTE]
55+
> 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.
56+
57+
You can create a tuple by assigning a value to each named member:
58+
59+
[!code-csharp[NamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#NamedTuple "Named tuple")]
60+
61+
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).
62+
63+
In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:
64+
65+
[!code-csharp[ImplicitNamedTuple](~/samples/snippets/csharp/new-in-7/program.cs#ImplicitNamedTuple "Implicitly named tuple")]
66+
67+
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.
68+
69+
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.
70+
71+
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:
72+
73+
[!code-csharp[TupleReturningMethod](~/samples/snippets/csharp/new-in-7/program.cs#TupleReturningMethod "Tuple returning method")]
74+
75+
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:
76+
77+
[!code-csharp[CallingWithDeconstructor](~/samples/snippets/csharp/new-in-7/program.cs#CallingWithDeconstructor "Deconstructing a tuple")]
78+
79+
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:
80+
81+
[!code-csharp[DiscardTupleMember](~/samples/snippets/csharp/new-in-7/program.cs#DiscardMember "Discard a tuple member")]
82+
83+
You can learn more in depth about tuples in the [tuples article](../../tuples.md).
84+
You can learn more about discards in the [discards article](../../discards.md).
85+
86+
- title: Use the type pattern with the is expression
87+
durationInMinutes: 2
88+
content: |
89+
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.
90+
91+
Try the following code in your browser window:
92+
93+
[!code-csharp[SimpleIs](~/samples/snippets/csharp/new-in-7/patternmatch.cs#SimpleIsPattern "Simple Is pattern")]
94+
95+
Change the variable declaration to a string instead:
96+
97+
```csharp
98+
object count = "5";
99+
```
100+
101+
Now, the `is` expression is false, so the `else` branch is executed. Try to change `count` to `number` in the else branch:
102+
103+
```csharp
104+
Console.WriteLine($"{number} is not an integer");
105+
```
106+
107+
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.
108+
109+
> [!NOTE]
110+
> There is an [issue](https://github.com/dotnet/try/issues/175) where you may get incorrect output in the preceding example.
111+
112+
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.
113+
114+
- title: Pattern matching in the switch statement
115+
durationInMinutes: 2
116+
content: |
117+
The *match expression* has a familiar syntax, based on the `switch`
118+
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:
119+
120+
[!code-csharp[SimpleSwitch](~/samples/snippets/csharp/new-in-7/patternmatch.cs#SimpleSwitchPattern "simple switch")]
121+
122+
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:
123+
124+
[!code-csharp[AddLongCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestLong "Add a case for long")]
125+
126+
The `switch` expression will convert a nullable type to its corresponding type. Add the following to verify:
127+
128+
[!code-csharp[NullableCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#NullableSwitch "Add a nullable case")]
129+
130+
You can add any number of other type pattern expressions to the switch statements. Add these before the `null` case:
131+
132+
[!code-csharp[MoreTypeCases](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreCases "Add more type cases")]
133+
134+
Make sure these work by adding the following tests:
135+
136+
[!code-csharp[AddMoreTests](~/samples/snippets/csharp/new-in-7/patternmatch.cs#MoreTests "Add more type tests")]
137+
138+
The match expressions also support constants. This can save time by factoring out simple cases:
139+
140+
[!code-csharp[ConstantCase](~/samples/snippets/csharp/new-in-7/patternmatch.cs#ConstantCase "Add a constant case")]
141+
142+
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.
143+
144+
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:
145+
146+
[!code-csharp[WhenClause](~/samples/snippets/csharp/new-in-7/patternmatch.cs#WhenClause "Add a when clause")]
147+
148+
Test it with something like the following code:
149+
150+
[!code-csharp[TestWhenClause](~/samples/snippets/csharp/new-in-7/patternmatch.cs#TestWhenClause "Test the when clause")]
151+
152+
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.
153+
154+
You can learn more about pattern matching in the article dedicated to [pattern matching in C#](../../pattern-matching.md).
155+
156+
- title: Optimize memory storage using ref locals and returns
157+
durationInMinutes: 2
158+
content: |
159+
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:
160+
161+
```csharp
162+
public class Program
163+
{
164+
private static (int i, int j) Find(int[,] matrix, Func<int, bool> predicate)
165+
{
166+
for (int i = 0; i < matrix.GetLength(0); i++)
167+
for (int j = 0; j < matrix.GetLength(1); j++)
168+
if (predicate(matrix[i, j]))
169+
return (i, j);
170+
return (-1, -1); // Not found
171+
}
172+
173+
public static void Main()
174+
{
175+
int[,] sourceMatrix = new int[10, 10];
176+
for (int x = 0; x < 10; x++)
177+
for (int y = 0; y < 10; y++)
178+
sourceMatrix[x, y] = x * 10 + y;
179+
180+
var indices = Find(sourceMatrix, (val) => val == 42);
181+
Console.WriteLine(indices);
182+
sourceMatrix[indices.i, indices.j] = 24;
183+
}
184+
185+
}
186+
```
187+
188+
189+
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.
190+
191+
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.
192+
193+
Start by modifying the `Find` method declaration so that it returns a `ref int` instead of a tuple.
194+
195+
```csharp
196+
private static ref int Find(int[,] matrix, Func<int, bool> predicate)
197+
```
198+
199+
Modify the return statement to return the item at the correct indices:
200+
201+
```csharp
202+
return matrix[i,j];
203+
```
204+
205+
Change the final return to throw an exception instead:
206+
207+
```csharp
208+
throw new InvalidOperationException("Not found");
209+
```
210+
211+
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:
212+
213+
```csharp
214+
return ref matrix[i,j];
215+
```
216+
217+
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:
218+
219+
```csharp
220+
var valItem = Find(matrix, (val) => val == 42);
221+
Console.WriteLine(valItem);
222+
valItem = 24;
223+
Console.WriteLine(matrix[4, 2]);
224+
```
225+
226+
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
227+
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.
228+
229+
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:
230+
231+
```csharp
232+
public static void Main()
233+
{
234+
int[,] sourceMatrix = new int[10, 10];
235+
for (int x = 0; x < 10; x++)
236+
for (int y = 0; y < 10; y++)
237+
sourceMatrix[x, y] = x * 10 + y;
238+
239+
ref var item = ref Find(sourceMatrix, (val) => val == 42);
240+
Console.WriteLine(item);
241+
item = 24;
242+
Console.WriteLine(sourceMatrix[4, 2]);
243+
}
244+
```
245+
246+
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.
247+
248+
The C# language has three other rules that protect you from misusing the `ref` locals and returns:
249+
250+
* You cannot assign a standard method return value to a `ref` local variable.
251+
* You cannot return a `ref` to a variable whose lifetime does not extend beyond the execution of the method.
252+
* `ref` locals and returns can't be used with async methods.
253+
254+
The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values or performing dereferencing operations multiple times.
255+
256+
For more information, see the [ref keyword](../../language-reference/keywords/ref.md) article.
257+
258+
- title: Minimize access to code with local functions
259+
durationInMinutes: 2
260+
content: |
261+
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:
262+
263+
- Recursive functions.
264+
- Iterator methods.
265+
- Asynchronous methods.
266+
267+
Let's start with recursive methods. Try the following code in the browser to calculate `6!` (factorial):
268+
269+
```csharp
270+
int LocalFunctionFactorial(int n)
271+
{
272+
return nthFactorial(n);
273+
274+
int nthFactorial(int number) => (number < 2) ?
275+
1 : number * nthFactorial(number - 1);
276+
}
277+
278+
Console.WriteLine(LocalFunctionFactorial(6));
279+
```
280+
281+
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.
282+
283+
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:
284+
285+
[!code-csharp[IteratorMethod](~/samples/snippets/csharp/new-in-7/Iterator.cs#SnippetIteratorMethod "Iterator method")]
286+
287+
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:
288+
289+
[!code-csharp[IteratorMethodRefactored](~/samples/snippets/csharp/new-in-7/Iterator.cs#IteratorMethodLocalInteractive "Iterator method refactored")]
290+
291+
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.
292+
293+
The same technique can be employed with `async` methods to ensure that exceptions arising from argument validation are thrown before the asynchronous
294+
work begins.
295+
296+
> [!NOTE]
297+
> Some of the designs that are supported by local functions
298+
> could also be accomplished using *lambda expressions*. Those
299+
> interested can [read more about the differences](../../local-functions-vs-lambdas.md).
300+
301+
- content: |
302+
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.
303+

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ms.date: 12/12/2018
88

99
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.
1010

11-
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.
11+
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.
1212

1313
## Read-only auto-properties
1414

0 commit comments

Comments
 (0)