Esse é o 7º post da série sobre C# 7. Pra acompanhar a série você pode seguir a tag C#7 no blog ou voltar no post que agrega a série.

Discards

No C#7 foi criada uma forma de jogar fora um valor, de descartar um valor. Isso veio com o uso de um caractere muito utilizado em outras linguagens, o _.

Existem alguns contextos onde isso é interessante, onde você quer descartar um valor, especificamente na hora de obter valores a partir de descontrução (de objetos ou tuplas), pattern match, no retorno de valores com out, ou simplesmente em um lugar onde você não quer guardar algum valor, de forma livre. O último caso é menos comum e interessante, então vamos ver os 3 primeiros.

O importante nos exemplos a seguir é entender que a variável _ não aloca memória e é write only, ou seja, você não consegue ler seu valor (lógico, ele foi descartado).

Desconstrução

Imagine que você tem uma função que separa um array entre o primeiro item e os restantes, mais conhecidos como head e tail, mas que, em uma operação específica, você precisa apenas da head. Para economizar memória, deixar o código mais limpo e não acabar alocando uma variável desnecessária, que poderia gerar confusão e bugs, descartamos o tail, colocando o valor na variável de descarte, a _.

var (head, _) = Separa(new[] { 1, 2, 3 });

Pattern matching

Em pattern matching você pode também ter valores que não lhe importam. Lembrando da função que separa head e tail como falei antes, vamos ver sua implementação usando pattern matching e com descarte do _:

(T head, T[] tails) Separa<T>(T[] array)
{
    switch (array)
    {
        case null:
        case object _ when array.Length == 0:
            return (default, new T[] { });
        case object _ when array.Length == 1:
            return (array[0], new T[] { });
        default:
            return (array[0], array.Skip(1).ToArray());
    }
}

Note que nos segundo e terceiros case não estou checando o tipo, apenas checando o tamanho do array, então estou descartando o valor encontrado no match; Não me importa o fato de ele ser um object, então descarto-o.

O descarte com pattern matching também funciona com if, assim como com a descontrução de objetos.

Observação: Essa é uma implementação de exemplo, eu provavelmente a escreveria de outra forma, mas ela cumpre o objetivo deste artigo.

Parâmetros out

Em métodos que retornam valores via parâmetros out, você pode descartar o valor passando-o para o _. No exemplo a seguir, quero validar se uma string pode ser convertida para um número, mas não me importa o número, então discarto-o:

if (int.TryParse("1", out var _)) WriteLine("É um número");

Observações

Note que se houver uma variável _ declarada e em contexto, ela terá prioridade, ou seja, ela receberá o valor, e a memória será alocada.

Além disso, note que a variável _ pode assumir qualquer tipo, e pode receber vários valores incompatíveis entre si, e se você possui um _ declarado, o tipo dessa variável poderá ser imcompatível com o valor que você quer descartar.

Por exemplo, imagine que a função a seguir retorna uma tupla com um inteiro e uma string, e quero descartar ambos:

var (inteiro, texto) = Obtem();
var (_, _) = Obtem();

A primeira chamada não descarta nenhum valor, já na segunda ambos são descartados. Mas, se você já tiver um _ declarado, ela vai falhar, por exemplo, neste caso, o código não compila:

int _;
var (_, _) = Obtem();

Você consegue ler sobre discards nos docs de Discards no Microsoft Docs.

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.