This repository has been archived by the owner on Aug 31, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[C#] Implement new Concept Exercise: nullability (#992)
[C#] Implement new Concept Exercise: nullability (#992) Co-authored-by: Erik Schierboom <erik_schierboom@hotmail.com> Co-authored-by: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com> Co-authored-by: Jeremy Walker <jez.walker@gmail.com> Co-authored-by: wolf99 <wolf99@users.noreply.github.com> Co-authored-by: Mike May <mikemay.mdamay@googlemail.com> Co-authored-by: mikedamay <mikemay@blueyonder.co.uk> Co-authored-by: Derk-Jan Karrenbeld <derk-jan+github@karrenbeld.info> Co-authored-by: mikedamay <github-actions[bot]@users.noreply.github.com> Co-authored-by: Eli Flanagan <efx@users.noreply.github.com> Co-authored-by: wolf99 <wol99@users.noreply.github.com> Co-authored-by: Johan Berg <47738833+bergjohan@users.noreply.github.com> Co-authored-by: Rob Keim <robkeim@gmail.com> Co-authored-by: Sascha Mann <git@mail.saschamann.eu> Co-authored-by: wolf99 <281700+wolf99@users.noreply.github.com>
- Loading branch information
1 parent
796afdb
commit d5241ae
Showing
13 changed files
with
435 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
languages/csharp/exercises/concept/nullability/.docs/after.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
Sometimes we need to make it so that variables have no particular | ||
value, i.e. they are empty. In C#, this corresponds to the [literal | ||
`null`][null-keyword]. | ||
|
||
In this exercise, we saw the definition of [nullable | ||
types][nullable-types-tutorial], and how the compiler and runtime of | ||
C# help us dealing with `null` values. | ||
|
||
At compilation time, [the operator `?`][nullable-reference-types] will | ||
declare a variable as being *nullable*. The compiler will then try to | ||
help us avoiding calling methods on possibly `null` variables, by | ||
raising warnings. You can use [the operator | ||
`!`][null-forgiving-operator] to avoid warnings in places we are sure | ||
a nullable variable is not null, but the compiler cannot detect | ||
it. | ||
|
||
We can also use [the operators `??` and | ||
`??=`][null-coalescing-operator] to provide a default value for a | ||
nullable variable and [the operator `?.`][null-conditional-operator] | ||
to chain accesses to methods, properties or attributes of potentially | ||
`null` objects on an expression that evaluates to `null` instead of | ||
throwing a `NullReferenceException`. | ||
|
||
```csharp | ||
string? userName = null; // declares `userName` as a nullable string | ||
Console.WriteLine(userName?.Length); // prints: "null", since `userName` is `null` | ||
Console.WriteLine(userName ?? "default"); // prints: "default", since `userName` is `null` | ||
userName ??= "unknownUser"; // sets `userName` to "unknownUser" since its | ||
// value was null | ||
Console.WriteLine(userName!.Length); // prints "11" and avoids a compiler warning | ||
``` | ||
|
||
At run time, calling any method or property on a | ||
`null` value throws a `NullReferenceException` exception. | ||
That is why it is important to always check if a nullable variable | ||
is `null` before calling methods on its value. | ||
|
||
# Important changes in C# 8.0 | ||
|
||
Sometimes, we need to make sure that some variables are never | ||
`null`. This simplifies code because it won't need to handle | ||
`NullReferenceException`s or provide extra provisions for `null` | ||
values. | ||
|
||
Before C# 8.0, reference types were nullable by default. For example, | ||
a variable of type `string` may contain a `null` value, even it is not | ||
declared as `string?`. | ||
|
||
For more information, refer to [this | ||
][nullable-csharp-8] and [this][nullable-reference-types-tutorial] documents. | ||
|
||
[null-keyword]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/null | ||
[nullable-types-tutorial]: https://csharp.net-tutorials.com/data-types/nullable-types/ | ||
[nullable-reference-types]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references | ||
[nullable-csharp-8]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references | ||
[null-forgiving-operator]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving | ||
[null-coalescing-operator]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator | ||
[null-conditional-operator]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and- | ||
[nullable-reference-types-tutorial]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2018/february/essential-net-csharp-8-0-and-nullable-reference-types |
38 changes: 38 additions & 0 deletions
38
languages/csharp/exercises/concept/nullability/.docs/hints.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
### General | ||
|
||
The following reference documentation may help you finishing this exercice: | ||
|
||
- [Null keyword][null-keyword]: reference documentation for `null` keyword. | ||
- [Nullable types tutorial][nullable-types-tutorial]: basic tutorial on how to work with nullable types. | ||
- [Nullable reference types][nullable-reference-types]: how to use nullable reference types. | ||
- [Null-coalescing operator][null-coalescing-operator]: explains how the null-coalescing operator works. | ||
- [Null-conditional operator][null-conditional-operator]: explains how the null-conditional operator works. | ||
- [Nullable reference types tutorial][nullable-reference-types-tutorial]: tutorial on nullable reference types. | ||
|
||
### 1. Badge.Label | ||
|
||
* Which method from the `string` class may help you making the | ||
department name [uppercase][toupper]? | ||
|
||
### 2. Badge.Label optional parameters | ||
|
||
* Do not forget to add `GUEST` as the department name if there's no | ||
provided department name. | ||
|
||
### 2. Badge.PrintLabel | ||
|
||
* Which method from the `string` class may help you to extract a | ||
[substring][substring] | ||
of a string? | ||
* Do all slices always have the same length? If not, how can you handle | ||
this case? | ||
* Do not forget the `prefix` should fit on the `maximumWidth` | ||
|
||
[null-keyword]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/null | ||
[nullable-types-tutorial]: https://csharp.net-tutorials.com/data-types/nullable-types/ | ||
[null-coalescing-operator]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator | ||
[null-conditional-operator]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator | ||
[nullable-reference-types]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references | ||
[nullable-reference-types-tutorial]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2018/february/essential-net-csharp-8-0-and-nullable-reference-types | ||
[substring]: https://docs.microsoft.com/en-us/dotnet/api/system.string.substring?view=netframework-4.8 | ||
[toupper]: https://docs.microsoft.com/en-us/dotnet/api/system.string.toupper?view=netframework-4.8xv |
37 changes: 37 additions & 0 deletions
37
languages/csharp/exercises/concept/nullability/.docs/instructions.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
In this exercise you'll be writing code to helping printing name | ||
badges for factory employees. | ||
|
||
Employees have an ID, name and department name. Badge labels are | ||
formatted as follows: `"[id] - [name] - [DEPARTMENT]"` | ||
|
||
### 1. Generate an employee's badge label | ||
|
||
Implement the `Badge.Label()` method to return an employee's badge label: | ||
|
||
```csharp | ||
Badge.Label(734, "Ernest Johnny Payne", "Strategic Communication"); | ||
// => "[734] - Ernest Johnny Payne - STRATEGIC COMMUNICATION" | ||
``` | ||
|
||
### 2. Handle the optional parts of a badge label | ||
|
||
The ID and department name are optional. If someone does not have an | ||
ID, do not print this part of the label. If someone has no | ||
department, just print `GUEST` as their department name. | ||
|
||
### 3. Generate the actual text to be print on the badge | ||
|
||
Implement the `Badge.PrintLabel()` method to return an employee's | ||
badge label that can fit on a badge with a given width. An optional | ||
prefix may be added on the beginning of each line: | ||
|
||
```csharp | ||
Badge.PrintLabel(" > ", "[734] - Ernest Johnny Payne - STRATEGIC COMMUNICATION", 30); | ||
// => " > [734] - Ernest Johnny Payne - \n> STRATEGIC COMMUNICATION" | ||
// | ||
// => Printed label: | ||
// | ||
// > [734] - Ernest Johnny Payne - | ||
// > STRATEGIC COMMUNICATION | ||
``` |
96 changes: 96 additions & 0 deletions
96
languages/csharp/exercises/concept/nullability/.docs/introduction.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
In C#, the `null` literal is used to denote the absence of a value. A | ||
*nullable* type is a type that allows `null` values. By default | ||
reference types are nullable and value types are non nullable. The | ||
`?` suffix to a variable's type makes it nullable if it is not. From | ||
C# 8.0 on, one can [make reference types non nullable by | ||
default][nullable-csharp-8], just like value types. | ||
|
||
```csharp | ||
int? nullable = 5; | ||
nullable = null; | ||
|
||
int nonNullable = 5; | ||
|
||
// Would result in compile error as type is not nullable | ||
// nonNullable = null; | ||
``` | ||
|
||
A common mistake is to access a member (e.g. a method or property) on a nullable | ||
variable whose value is `null`. This will cause the C# runtime | ||
to raise a `NullReferenceException`. | ||
|
||
The C# compiler can help us avoiding such errors. If we try to | ||
compile the following code: | ||
|
||
```csharp | ||
string? userName = ...; | ||
|
||
... | ||
|
||
Console.WriteLine(userName.Length); | ||
``` | ||
|
||
The compiler will give the following warning message: | ||
|
||
``` | ||
Dereference of a possibly null reference. (CS8602) | ||
``` | ||
|
||
A good practice is testing if a nullable value is not `null` before | ||
trying to do something with it. There are some cases however, where we | ||
know for sure that, given the context, a variable cannot be `null`. | ||
|
||
In order to dismiss the warning, we can use the operator `!`: | ||
|
||
```csharp | ||
Console.WriteLine(userName!.Length); | ||
``` | ||
|
||
Finally, sometimes, we want to provide a default value to a variable, | ||
in case it is `null`. We know how to use an `if` block to check | ||
that. However, the more nullable variables we need to check, the more | ||
cumbersome it gets. | ||
|
||
The `??` and `?.` operators are a simple shortcuts for that: | ||
|
||
```csharp | ||
// prints: "default" if `userName` is `null` | ||
Console.WriteLine(userName ?? "default"); | ||
|
||
// This code is equivalent to: | ||
string userNameValue; | ||
if (userName == null) | ||
{ | ||
userNameValue = "default"; | ||
} | ||
else | ||
{ | ||
userNameValue = userName; | ||
} | ||
Console.WriteLine(userNameValue); | ||
|
||
|
||
|
||
// prints: `null` if `userName` is `null` | ||
Console.WriteLine(userName?.Length); | ||
|
||
// This code is equivalent to | ||
int userNameLength; | ||
if (userName == null) | ||
{ | ||
userNameLength = 0; | ||
} | ||
else | ||
{ | ||
userNameLength = userName.Length; | ||
} | ||
Console.WriteLine(userNameLength); | ||
``` | ||
|
||
With `A ?? B`, the C# runtime replaces the expression `A` with `B` if | ||
`A` evaluates to `null`. The operator `?.` behaves similarly, the C# | ||
runtime evaluates the expression `A?.B` by `null` if `A` is null, | ||
without throwing a `NullReferenceException`. It executes `A.B` | ||
otherwise. | ||
|
||
[nullable-csharp-8]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references |
27 changes: 27 additions & 0 deletions
27
languages/csharp/exercises/concept/nullability/.meta/Example.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System; | ||
|
||
public static class Badge | ||
{ | ||
public static string Label(int? id, string name, string? department) | ||
{ | ||
var idLabel = (id == null ? "" : $"[{id}] - "); | ||
return $"{idLabel}{name} - {department?.ToUpper() ?? "GUEST"}"; | ||
} | ||
|
||
public static string PrintLabel(string? prefix, | ||
string label, | ||
int maximumWidth) | ||
{ | ||
maximumWidth -= prefix?.Length ?? 0; | ||
|
||
var output = ""; | ||
for (int i = 0; i < label.Length; i += maximumWidth) | ||
{ | ||
output += (prefix ?? "") + | ||
label.Substring(i, Math.Min(maximumWidth, | ||
label.Length - i)) + "\n"; | ||
} | ||
return output.TrimEnd(); | ||
} | ||
|
||
} |
8 changes: 8 additions & 0 deletions
8
languages/csharp/exercises/concept/nullability/.meta/config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"authors": [ | ||
{ | ||
"github_username": "maurelio1234", | ||
"exercism_username": "maurelio1234" | ||
} | ||
] | ||
} |
50 changes: 50 additions & 0 deletions
50
languages/csharp/exercises/concept/nullability/.meta/design.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Design | ||
|
||
## Goal | ||
|
||
The goal of this exercise is to introduce the student to the concept of [Nullability in C#][null-keyword]. | ||
|
||
## Learning objectives | ||
|
||
- Know of the existence of the `null` literal. | ||
- Know what a `NullReferenceException` is and when it is thrown. | ||
- Know how to compare a value to `null`. | ||
- Know the difference between value and reference types regarding nullability, especially pre C# 8.0. | ||
- Know how to define nullable reference and value types. | ||
- Know about the null-related operators (`!`, `?`, `??`). | ||
- Know about basic null checking by the compiler. | ||
|
||
## Out of scope | ||
|
||
- Nullable attributes. | ||
- In-depth discussion of null checking by the compiler. | ||
- Enabling C# 8 null checking. | ||
- Casting using the `as` or `is` operator or using pattern matching. | ||
|
||
## Concepts | ||
|
||
This Concepts Exercise's Concepts are: | ||
|
||
- `nullability`: know of the existence of the `null` literal; know what a `NullReferenceException` is and when it is thrown; know how to compare a value to `null`; know the difference between value and reference types regarding nullability; know how to define nullable reference and value types; know about the null-related operators; know about basic null checking by the compiler. | ||
|
||
## Prerequisites | ||
|
||
This Concept Exercise's prerequisites Concepts are: | ||
|
||
- `strings`: strings will be compared to `null` and basic methods from strings will be called. | ||
- `basics`: integers will be compared to `null`, arithmetic operations will be performed on integers, variables will be introduced and updated. | ||
- `exceptions`: explain how a `NullReferenceException` is thrown when accessing a `null` value. | ||
- `for-loops`: strings will be processed and constructed iteratively. | ||
- `memory-allocation`: reference and value types will be used in their nullable and non-nullable variants. | ||
|
||
## Representer | ||
|
||
This exercise does not require any specific representation logic to be added to the [representer][representer]. | ||
|
||
## Analyzer | ||
|
||
This exercise does not require any specific logic to be added to the [analyzer][analyzer]. | ||
|
||
[analyzer]: https://github.com/exercism/csharp-analyzer | ||
[representer]: https://github.com/exercism/csharp-representer | ||
[null-keyword]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/null |
16 changes: 16 additions & 0 deletions
16
languages/csharp/exercises/concept/nullability/Nullability.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
|
||
public static class Badge | ||
{ | ||
public static string Label(int? id, string name, string? department) | ||
{ | ||
throw new NotImplementedException("Please implement the Badge.Label method"); | ||
} | ||
|
||
public static string PrintLabel(string? prefix, | ||
string label, | ||
int maximumWidth) | ||
{ | ||
throw new NotImplementedException("Please implement the Badge.PrintLabel method"); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
languages/csharp/exercises/concept/nullability/Nullability.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | ||
<PackageReference Include="xunit" Version="2.4.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.