Tony Hoare, em uma palestra na Qcon 2009 de Londres, reconheceu ter cometido um grande equívoco, há mais de 50 anos, projetando a capacidade de variáveis, de um determinado tipo, terem valor nulo, e, ao serem “desreferenciadas”, gerarem um valor especial indicando essa nulidade. Em C#, esse valor especial é null
.
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. – Tony Hoare
O problema é que é fácil escrever programas de computador ignorando a possível nulidade de uma variável. Em .NET, quando isso ocorre, há uma interrupção da capacidade do programa seguir seu fluxo padrão ocasionando uma exceção – NullReferenceException. Sem dúvidas, uma dos erros mais comuns em tempo de execução e, por isso, uma fonte gigantesca de custos.
Há tempos, analisadores estáticos tem conseguido identificar e indicar trechos de código que ignoram a possibilidade de nulidade ao desreferenciar variáveis indicando, inclusive, práticas de programação defensiva que impediriam a ocorrência de NullReferenceException. Entretanto, a adoção desses analisadores ainda é bem baixa.
Recentemente, o time de design de linguagens da Microsoft responsável pelo C#, que desde o início suporta o conceito de nulidade em suas referências, resolveu tentar ajudar a conter prejuízos causados. Para isso, introduziu uma mudança de sintaxe que aumenta a expressividade do código e ajuda o compilador a detectar falhas deixando a linguagem um pouco mais complexa.
Fundamentalmente, a ideia é fazer com que o programador deixe “explícito” se o código que escreveu está preparado para lidar com a nulidade ou não.
public void Foo(Customer? c) // este método não terá problemas se c for nulo. { if (c != null) { Console.WriteLine(c.Name); } } public void Fee(Customer c) // este método irá ter um "NullReferenceException" caso um nulo for passado. { Console.WriteLine(c.Name) }
Dessa forma, o compilador consegue ser inteligente o suficiente para detectar código que pode gerar instabilidade, gerando warnings no momento da compilação.
public Customer? GetCustomerById(string id) { //.. }
No código acima, por exemplo, o método GetCustomerById
deixa explícito que pode retornar um referência nula. Além disso, também está indicado que o método irá falhar, lançando uma exception, caso uma string nula seja passada como parâmetro. O compilador, então, tem condições de verificar se o retorno está sendo adequadamente tratado e se há chances de um valor nulo ser passado para o método, indicando reparação.
O problema, entretanto, é que esse conceito é novo e incompatível com o entendimento de código para C# até então. Um programador, embora experiente, que não esteja ambientado com essa mudança terá alguma dificuldade para entender o código e para produzir código novo em conformidade. Além disso, código escrito até aqui também não se encaixa nos critérios prescritos a partir de agora. Por isso, a adoção dessa característica é optativa e a Microsoft está tentando orquestrar o processo de adoção produzindo guias de orientação.
De qualquer forma, superadas as dores da adoção, uma das principais causas de erros em tempo de execução terá ficado para trás. É uma pena que essa feature não estivesse na especificação da linguagem desde sua primeira versão.
Elemar
Lendo esse seu post lembrou-me de um tutorial que havia em seu site falando de Monads.
Depois que o li, comecei a criar ‘safe methods’ abstratos, além de outros métodos de validação, como ‘.IsNull()’, .IsNotNull()’, para evitar que erros de NullReferenceException ocorressem. Desde essa época, lá por 2016, 2017 comecei a ter um cuidado maior que esse tipo de falha, que pode ser considerada até certo ponto como boba e que gera vários problemas e perdas.
Parabéns por trazer a nova info.
Não é algo novo, mas sem dúvida é extremamente essencial. A verificação de variáveis nulas são excelente para integridade do sistema, por isso tipos primitivos não aceitam nulos (com exceção do string).
É algo bem interessante de se ler, afinal, pouco se vê sobre isso.