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.