Novidades do C# 8

Olá, neste artigo eu vou escrever um resumo das novidades que eu acho mais relevantes que vieram no C# na versão 8.

Index:
Deconstruct

Antes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public sealed class LoggingConfiguration
{
public bool Enabled { get; set; }

public string? Path { get; set; }
}

public static class Logger
{
public static void LogInfo(in LoggingConfiguration config, in string message)
{
var configuration = config ?? new LoggingConfiguration();
if (!configuration.Enabled)
{
return;
}

Console.WriteLine($"{configuration.Path ?? "logs/.log"} => {message}");
}
}

Depois.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public sealed class LoggingConfiguration
{
public bool Enabled { get; set; }

public string? Path { get; set; }

public void Deconstruct(out bool enabled, out string path) => (enabled, path) = (
this.Enabled,
this.Path ?? "logs/.log");
}

public static class Logger
{
public static void LogInfo(in LoggingConfiguration config, in string message)
{
var (enabled, path) = config ?? new LoggingConfiguration();
if (!enabled)
{
return;
}

Console.WriteLine($"{path} => {message}");
}
}
using Declarations

Antes.

1
2
3
4
5
6
7
8
9
10
11
12
13
public static class Reader
{
public static Task<string> Read(in string path)
{
using (var stream = File.OpenRead(path))
{
using (var reader = new StreamReader(stream))
{
return reader.ReadToEndAsync();
}
}
}
}

Depois.

1
2
3
4
5
6
7
8
9
10
public static class Reader
{
public static Task<string> Read(in string path)
{
using var stream = File.OpenRead(path);
using var reader = new StreamReader(stream);

return reader.ReadToEndAsync();
}
}
static Local functions

Antes.

1
2
3
4
5
6
7
8
9
public void LogInfo(string message)
{
Log("Info");

void Log(in string level)
{
Console.WriteLine($"{level} -> {message}");
}
}

Depois.

1
2
3
4
5
6
7
8
9
public void LogInfo(in string message)
{
Log("Info", message);

static void Log(in string level, in string message)
{
Console.WriteLine($"{level} -> {message}");
}
}
Nullable reference types

Para habilitar a verificar de null reference types, adicione no seu csproj o código abaixo:

1
2
3
4
<PropertyGroup>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

Com este configuração o compilador vai começar a lançar warning em praticamente todo o trecho de código que pode conter null.

! Operator (que perdoa nulo)
1
2
3
4
5
6
7
8
9
10
11
public class User
{
private User(in string name)
{
this.Name = name ?? throw new ArgumentNullException(nameof(name), "Name cannot be null");
}

public string Name { get; }

public static User NewUser(string? name) => new User(name!);
}

Sem este operador o compilador iria mostrar o warning abaixo:

'name' may be null here.

Possible null reference argument for parameter 'name' in 'User.User(in string name)'.

Neste caso ele não precisaria mostrar este warning já que no construtor da classe User tem uma validação se o name é nulo, por isso neste caso podemos usar o operador !.

Asynchronous streams

Antes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class UserImporter
{
public static async Task<IEnumerable<User>> Import(string path)
{
await using var stream = File.OpenRead(path);
using var reader = new StreamReader(stream);

string? id;
var users = new List<User>();

while ((id = await reader.ReadLineAsync()) != null)
{
users.Add(await GetUserByIdQuery.GetResult(id));
}

return users;
}
}

public static class UserService
{
public static async Task Import(string path)
{
foreach (var user in await UserImporter.Import(path))
{
await InsertUserCommand.Execute(user);
}
}
}

Depois.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static class UserImporter
{
public static async IAsyncEnumerable<User> Import(string path)
{
await using var stream = File.OpenRead(path);
using var reader = new StreamReader(stream);

string? id;

while ((id = await reader.ReadLineAsync()) != null)
{
yield return await GetUserByIdQuery.GetResult(id);
}
}
}

public static class UserService
{
public static async Task Import(string path)
{
await foreach (var user in UserImporter.Import(path))
{
await InsertUserCommand.Execute(user);
}
}
}
Null-coalescing assignment

Antes.

1
2
3
4
5
6
7
8
9
public static class Logger
{
public static void LogInfo(string? path, in string message)
{
path = path ?? "logs/.log";

Console.WriteLine($"{path} => {message}");
}
}

Depois.

1
2
3
4
5
6
7
8
9
public static class Logger
{
public static void LogInfo(string? path, in string message)
{
path ??= "logs/.log";

Console.WriteLine($"{path} => {message}");
}
}
Mais exemplos

Abaixo uma lista de projetos que você pode ver as novidades do C# em exemplos reais.

Chess é uma console app feita com ASCII art.
🐷 Hawk (api) é sistema de finança pessoal.
Http.Query.Filter é uma lib para fazer filtros baseado no StrongLoop da IBM.

Obrigado pela visita 🙂!