Esse é o 22º post da série sobre C# 7, e o oitavo sobre C# 7.2, além do último 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.

Lembrando que para utilizar as versões minor do C# (como a 7.1, ou 7.2) você precisa habilitá-la nos atributos do projeto. Veja neste post como fazê-lo e também como habilitar na solution inteira pra não ter que ficar configurando cada projeto individualmente.

Novidades do C# 7.2: Operador ternário com ref

No C# 7.0 foi criada a variável local por referência, a ref local. Mas até o C# 7.2 não era possível usar o operador ternário com ref locals. A alternativa era usar um método como:

ref T Escolha(bool condicao, ref T consequencia, ref T alternativa)
{
    if (condicao)
         return ref consequencia;
    else
         return ref alternativa;
}

E em seguida, invocá-lo dessa forma:

ref var r = ref Escolha(arr != null, ref arr[0], ref outroArr[0]);

No entanto, esse código vai causar uma exception, porque todos os valores serão avaliados antecipadamente, ou seja, o arr, mesmo sendo nulo, vai ter sua primeira posição buscada, para que possa ser passado para o método Escolha. Nós gostaríamos que isso só acontecesse se ele não fosse nulo, ou seja, precisamos que essa avaliação seja lazy. É pra isso que o operador ternário serve, mas, como eu disse antes, até o C# 7.2 ele não podia ser usado com referências. A opção que sobrava era usar um horrendo if.

Com o C# 7.2 já temos a opção do operador ternário, então, o código acima ficaria:

ref var r = ref (arr != null ? ref arr[0] : ref outroArr[0]);

(Sim, ref pra todo lado!! Você achou que código eficiente era bonito?)

Pontos interessantes do operador ternário com ref

Como o resultado de um operador ternário com ref é uma referência podemos fazer coisas divertidas com ele, como passar um valor pra referência:

(arr != null ? ref arr[0] : ref outroArr[0]) = 1;

O exemplo acima vai setar o valor do array correto, sem cópia alguma.

Também podemos invocar métodos, sem cópia alguma de valores:

(arr != null ? ref arr[0] : ref outroArr[0]).MetodoDaEstrutura();

E, se o método for chamado sobre uma estrutura, não há cópia também. No exemplo abaixo, campoReadOnly é um campo somente leitura, e seu tipo é uma estrutura também somente leitura.

(arr != null ? ref arr[0] : ref outroArr[0].campoReadOnly).MetodoDaEstruturaReadonly();

Pra entender como essa cópia é evitada veja o post sobre estruturas imutáveis.

Você consegue ler um pouco mais sobre este assunto somente no Github e também neste outro artigo, também no Github. Não encontrei docs sobre isso no Microsoft Docs, então, só lá mesmo.

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.