Novidades do C# 7, 7.1, 7.2 e 7.3

Olá, neste artigo eu vou escrever um resumo das novidades que eu acho mais relevantes que vieram no C# nas versões 7, 7.1, 7.2 e 7.3.

out Variables

Antes.

C#
int result;
if (int.TryParse(input, out result))
{
    Console.WriteLine(result);
}

Depois.

C#
if (int.TryParse(input, out var result))
{
    Console.WriteLine(result);
}

Tuples

Declaração de variáveis, antes.

C#
var result = new Tuple<bool, string>(false, "An error has occurred");
Console.WriteLine($"{result.Item1}, {result.Item2}");

Depois.

C#
var result = (Successfully: false, Message: "An error has occurred");
Console.WriteLine($"{result.Successfully}, {result.Message}");

Chamada de método, antes.

C#
var result = DoSomething();
Console.WriteLine($"{result.Item1}, {result.Item2}");

Tuple<bool, string> DoSomething() => new Tuple<bool, string>(false, "An error has occurred");

Depois.

C#
var (successfully, message) = DoSomething();
Console.WriteLine($"{successfully}, {message}");

(bool Successfully, string Message) DoSomething() => (false, "An error has occurred");

Discards

  • não aloca memória;
  • é write only;

variáveis out que não serão usadas, antes.

C#
var successfully = int.TryParse(input, out var result);

Depois.

C#
var successfully = int.TryParse(input, out _);

variáveis em pattern matching que não serão usadas, antes.

C#
switch (true)
{
    case object n when successfully:
        return "Successfully";
    default:
        return "An error has occurred";
}

Depois.

C#
switch (true)
{
    case object _ when successfully:
        return "Successfully";
    default:
        return "An error has occurred";
}

variáveis desconstruídas que não serão usadas, antes.

C#
var (successfully, message) = DoSomething();
if (successfully)
{
    return;
}

Depois.

C#
var (successfully, _) = DoSomething();
if (successfully)
{
    return;
}

Local functions

  • pode ser async;
  • pode usar iterators com yield;
  • pode conter outra função local;
  • pode conter um bloco ou uma expressão;
  • pode ser genérica e pode restringir os tipos genéricos;
  • pode usar variáveis e parâmetros genéricos que estejam no escopo da função que ela foi criada;
  • pode usar parâmetros ref e out;
  • não pode mudar o fluxo da função externa, por exemplo com uso de goto, break, continue e etc;
  • não causam alocações locais como declaração de Func ou Action;

Antes.

C#
public void LogInfo(in string message)
{
    Log("Info", message);
}

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

Depois.

C#
public void LogInfo(string message)
{
    Log("Info");

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

Mais expression-bodied

Esta feature feio da versão 6, e na versão 7 eles adicionaram em construtures, finalizadores e propriedades.

Antes.

C#
public class User
{
    private readonly string name;

    public User(in string name)
    {
        this.name = name;
    }

    ~User()
    {
        Console.WriteLine("User finalized.");
    }

    public string Name
    {
        get
        {
            return this.name?.ToUpper() ?? string.Empty;
        }
    }
}

Depois.

C#
public class User
{
    private readonly string name;

    public User(in string name) => this.name = name;

    ~User() => Console.WriteLine("User finalized.");

    public string Name => this.name?.ToUpper() ?? string.Empty;
}

throw Expressions

Agora podemos utilizar throw em uma operação de null coalesce e em expression bodied members.

Antes.

C#
public class User
{
    private string name;

    public string Name
    {
        set
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value), "Name cannot be null");
            }

            this.name = value;
        }

        get
        {
            throw new NotImplementedException();
        }
    }
}

Depois.

C#
public class User
{
    private string name;

    public string Name
    {
        set => this.name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
        get => throw new NotImplementedException();
    }
}

async Método Main

Antes.

C#
public class Program
{
    public static void Main(string[] args) => MainAsync()
        .GetAwaiter()
        .GetResult();

    private static Task MainAsync() => Task.CompletedTask;
}

Depois.

C#
public class Program
{
    public static Task Main(string[] args) => Task.CompletedTask;
}

default Literal expressions

Em parâmetros opcionais, antes.

C#
public class User
{
    public User(in string name)
    {
        this.Id = default(uint);
        this.Name = name;
    }

    public uint Id { get; private set; }

    public string Name { get; }

    public override string ToString() => default(string);

    public User UpdateUser(in uint id, in string name = default(string)) => new User(name ?? this.Name)
    {
        Id = id,
    };
}

Depois.

C#
public class User
{
    public User(in string name)
    {
        this.Id = default;
        this.Name = name;
    }

    public uint Id { get; private set; }

    public string Name { get; }

    public override string ToString() => default;

    public User UpdateUser(in uint id, in string name = default) => new User(name ?? this.Name)
    {
        Id = id,
    };
}

Argumentos nomeados não finais

Antes.

C#
UpdateUser(id: 1, name: name);

Depois.

C#
UpdateUser(id: 1, name);

Técnicas para escrever código eficiente e seguro

in o parâmetro é passado por referência só que readonly; ref o parâmetro é passado por referência read and write; ref readonly o retorno do método é por referência e não pode ser alterado; struct readonly a estrutura é readonly evitando quando um método é acessado uma cópia desnecessária; protected internal permite classes derivadas ou no mesmo assembly; private protected permite classes derivadas e no mesmo assembly;

Nota

Pattern Matching terá um artigo só para ele, e será sempre atualizado com as novas versões do C#.

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 🙂!

Comentários