VAGAS DE EMPREGO

balta.io balta.io
  • Cursos
  • Carreiras
  • Para sua Empresa
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog

Seja Premium
balta.io

  • Cursos
  • Carreiras
  • Para sua Empresa
  • Agenda
  • Livros
    • Background Services
    • Blazor com .NET 8
    • Segurança em APIs
    • Futuro do C# 12
    • Nullable Types
    • Clean Code
  • Blog
  • Player
Seja Premium

Entre ou Cadastre-se

  • Home
  • Artigos
  • Entendendo Nullable Types no .NET


👉 Temos uma versão mais atualizada deste artigo no nosso novo Blog

Entendendo Nullable Types no .NET

Lidar com possíveis valores nulos em qualquer linguagem de programação é sempre um desafio e o .NET/C# tem uma ótima forma de te ajudar com isto.

O problema

Se você atualizou seu projeto para .NET 6 ou superior ou mesmo se começou um projeto recentemente, é possível que tenha visto diversos warnings (Avisos) sobre possíveis referências nulas em seu código.

warning CS8618: Non-nullable property 'XPTO' must contain a non-null value when exiting constructor

Embora este aviso não previna que sua aplicação seja compilada, ele indica que pode haver um possível valor nulo em seu código, que causa o famoso NullReferenceException.

O que mudou no .NET 6?

Estes avisos só são apresentados em versões 6 ou superiores do .NET por conta de uma nova configuração adicionada aos arquivos .csproj, chamada <Nullable>, conforme mostrada abaixo.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable> <!-- 👈-->
  </PropertyGroup>
</Project>

Embora seja plenamente possível, remover esta configuração do projeto ou mesmo atribuir o valor Disable para ela, a recomendação é que ela seja mantida.

O que é NULL?

Antes de falar sobre como resolver este problema no código, precisamos entender o que é o NULL e quais reais problemas temos com eles.

O .NET possui duas categorias de tipos, os tipos de referência (Reference Types) e os tipos de valor (Value Types). É importante frisar que todo tipo de valor é inicializado com um valor padrão diferente de NULL.

Na verdade, qualquer tipo pode ser nulo e vamos entender isso mais adiante, mas todo tipo de valor no .NET é inicializado com o que chamamos de valor padrão.

No caso dos números (int, double, float, decimal), este valor é zero, no caso de um booleano o valor é false, no caso de um char o valor é \0 e assim por diante.

Mesmo quando temos tipos complexos como estruturas (que são tipos de valor), nós temos um valor padrão sendo atribuído a elas.

Um bom exemplo é o DateTime, que se nenhum valor for atribuído, o mesmo recebe 1/1/0001 12:00:00 AM.

Por que um tipo de valor não é nulo?

Se você já teve a oportunidade de fazer nosso CURSO GRATUITO DE C#, aprendeu que os tipos no .NET tem um tamanho específico. Por exemplo, um int sempre vai ocupar 32 bits, um short sempre vai ocupar 16 bits e assim por diante.

Essa tipagem ajuda muito na alocação de memória, afinal, todo programa vai para lá antes de ser executado e saber que uma determinada estrutura precisa de X bytes na memória, já ajuda a organizar os dados.

Desta forma, não faz sentido algum inicializar um tipo de valor com nulo. Qual a vantagem em dizer ao computador que precisamos de 4 bytes da memória e não colocar nenhum valor lá?

Tipos de referência são os vilões

Então sabemos que os tipos de valor armazenam uma informação, mas e os tipos de referência, aliás o que são eles?

Diferente dos tipos de valor, os tipos de referência armazenam apenas um endereço, um referência para onde os dados possivelmente estarão.

Este comportamento ocorre pois com referências temos um dinamismo maior nos tipos, nos permitindo aumentar e diminuir seu tamanho.

Um ótimo exemplo de tipo de referência são as strings que nada mais são do que um array de char.

Nós podemos ter strings (Listas no geral) de qualquer tamanho, desta forma, faz todo sentido trabalharmos com tipo de referência para elas.

Então, na verdade, quando temos uma string, temos apenas uma referência para seu conjunto de caracteres que está em algum lugar na memória.

Claro que tem muito mais coisa abaixo desta explicação como constantes, literais estáticas, mas você pegou a ideia.

Resumindo, quando declaramos uma string, dizemos “Olha, do endereço 1 ao 27 estão as informações desta string”.

E qual problema de ter uma referência nula?

O maior problema em ter uma referência nula é tentar acessar seu valor. Sempre que fazemos isto no .NET, recebemos uma exceção do tipo NullReferenceException.

Um caso clássico é quando estamos trabalhando com listas e esquecemos de inicializá-las:

public class Order
{
	public List<Product> Products { get; set: ]
	// ⚠️ Lista não inicializada, vai dar erro
}

var order = new Order();
order.Products.Add(product);
// ❌ NullReferenceException: Products é NULL

Isto vale para todo tipo de referência, e como consequência disso, temos que sempre verificar se o objeto está apontando para nulo antes de informar algum valor ou tentar acessar sua informação.

💡 APRENDA C# DE GRAÇA com o curso Fundamentos do C#. São 144 aulas divididas em 14 módulos com mais de 12 horas de duração, TOTALMENTE GRATUITO e com certificado de conclusão.

Resolvendo o problema com NULLs

O .NET resolveu incluir a opção que mencionei no começo do artigo para nos avisar de possíveis referências nulas e assim podermos tratá-las ainda em tempo de compilação.

Para seguir adiante, vamos tomar a classe abaixo como exemplo:

public class Student
{
	public string Name { get; set; } 
	// ⚠️ Non-nullable property 'Name' is uninitialized
}

Inicializando as propriedades

Conforme vimos, como Name é do tipo string, precisamos inicializá-lo, e para tipos primitivos, isto é relativamente simples.

public class Student
{
	public string Name { get; set; } = string.Empty;
	// ✅ OK
}

public class Student
{
	public string Name { get; set; } = "André";
	// ✅ OK
}

Também podemos fazer a inicialização via construtor, o compilador consegue identificar que a propriedade não foi inicializada imediatamente, mas será quando o objeto for construído.

public class Student
{
	public Student()
	{
		Name = string.Empty;
		// ✅ OK
	}

	public string Name { get; set; } // ✅ OK
}

É importante salientar que caso tenha múltiplos construtores, você precisa inicializar sua propriedade em todos eles. Então talvez seja mais fácil utilizar o primeiro exemplo desta sessão.

public class Student
{
	protected Student()
	{		
		// ⚠️ Continuará com o Warning pois não foi inicializado aqui.
	}

  public Student(string name)
	{
		Name = name;
		// ✅ OK
	}

	public string Name { get; set; } // ⚠️
}

Nullable Types

E se por acaso eu quiser que a propriedade Name seja nula? Isto é possível e utilizado principalmente quando trabalhamos com Entity Framework por exemplo.

Neste caso, o .NET inclui uma opção bem simples e elegante, bastando adicionar ? ou utilizar Nullable<T> na propriedade.

public Nullable<DateTime> UpdatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }

Tanto faz utilizar Nullable<DateTime> ou DateTime?, o ? é apenas um syntax sugar (enfeite ou apelido) para o Nullable<DateTime>.

Em resumo, sempre que você quiser deixar explícito que um tipo pode receber um valor nulo, utilize o ? na frente do tipo.

public class Student
{
    public Name? Name { get; set; }
    public DateTime? UpdatedAt { get; set; }
}

public class Name
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

Desta forma você também não terá o aviso do compilador, já que você mesmo explicitou no código que quer receber valores nulos para aquelas propriedades.

Null NOT

Como nada na vida é simples, vamos supor que temos a seguinte situação:

public class Student
{
    public Name Name { get; set; } 
		// ⚠️ Non-nullable property 'Name' is uninitialized
    public DateTime? UpdatedAt { get; set; }
}

public class Name
{
    public Name(string firstName, string lastName)
    {
        if (string.IsNullOrEmpty(firstName) ||
            string.IsNullOrEmpty(lastName))
            throw new ArgumentNullException("First name and last name are required");

        FirstName = firstName;
        LastName = lastName;
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Neste cenário, temos uma classe que define o nome e utilizamos ela na classe Student, cujo o nome NÃO PODE SER NULO.

Para piorar, não conseguimos instanciar um Name sem informar o primeiro e último nome, que são obrigatórios devido a falta do construtor sem parâmetros (parameterless) que o Name possui.

public Name Name { get; set; } = new();
// ❌ Não funciona pois precisamos do primeiro e último nome para instanciar o Name

public Name Name { get; set; } = new("", "");
// ❌ Não podemos inicializar com strings vazias

public Name Name { get; set; } = new("Bruce", "Wayne");
// ❌ Não queremos ter um valor padrão

Este cenário é a definição perfeita para o uso do NULL NOT ou null! (Null com uma exclamação na frente).

O NULL NOT nos permite dizer ao compilador o seguinte:

Esta propriedade não pode ser nula mas eu não tenho um valor para ela neste momento. No entanto eu me comprometo a inicializar ela antes de utilizá-la.

Basicamente nós puxamos a responsabilidade e garantimos que este valor, embora nulo no momento, não será no futuro, que ele será inicializado.

public Name Name { get; set; } = null!;
// ✅ OK

É importante frisar que o NULL NOT não tem relação com o NOT NULL. O NOT NULL é um comparador que possui implementação e finalidade diferente.

if(name is not null)
	...

if(name != null)
	...

Conclusão

Entender como os valores nulos são criados e onde são utilizados é fundamental, bem como manter as configurações que ajudam o compilador a identificar possíveis referências nulas.

O .NET possui recursos incríveis tanto para manipulação de tipos nulos quanto para tratativa de referências nulas.

Utilize o compilador e as configurações ao seu favor, otimize seu código, evite excesso de condicionais e divirta-se com o conteúdo que aprendeu aqui 🚀

Populares

Priority Queue

Priority Queue ou fila prioritária é um tipo de lista inclusa no C# 10 que permite que seus itens...


Implicit Operators no C#

Implicit Operators permitem adicionar comportamentos de conversão a objetos Built In ou complexos...


ASP.NET 5 – Autenticação e Autorização com Bearer e JWT

Este artigo atualmente utiliza a versão 5.0.0-rc.1 do ASP.NET/.NET, o que significa que ainda não...


Clean Code - Guia e Exemplos

Saiba como manter seu código limpo (Clean Code) seguindo algumas práticas sugeridas pelo Robert C...


Git e GitHub - Instalação, Configuração e Primeiros Passos

Git é um sistema de controle de versões distribuídas, enquanto GitHub é uma plataforma que tem o ...


Compartilhe este artigo



Conheça o autor

André Baltieri

André Baltieri

Microsoft MVP

Me dedico ao desenvolvimento de software desde 2003, sendo minha maior especialidade o Desenvolvimento Web. Durante esta jornada pude trabalhar presencialmente aqui no Brasil e Estados Unidos, atender remotamente times da ?ndia, Inglaterra e Holanda, receber 8x Microsoft MVP e realizar diversas consultorias em empresas e projetos de todos os tamanhos.





3.133

Aulas disponíveis

292

horas de conteúdo

76.647

Alunos matriculados

53.197

Certificados emitidos





Comece de graça agora mesmo!

Temos mais de 21 cursos totalmente de graça e todos com certificado de conclusão.

Começar


Prefere algo mais Premium?

Conheça nossos planos



Premium anual

Compra única, parcelada em até
12x no cartão de crédito


12x R$

99

,79

=R$ 1.197,44
  • 1 ano de acesso
  • Acesso à todo conteúdo
  • Emissão de Certificado
  • Tira Dúvidas Online
  • 67 cursos disponíveis
  • 10 carreiras disponíveis
  • 161 temas de tecnologia
  • Conteúdo novo todo mês
  • Encontros Premium

Começar agora

Política de privacidade



Precisa de ajuda?

Dúvidas frequentes



  • Posso começar de graça?

    Sim! Basta criar sua conta gratuita no balta.io e começar seus estudos. Nós contamos com diversos cursos TOTALMENTE gratuitos e com certificado de conclusão.

  • Vou ter que pagar algo?

    Nós temos cursos gratuitos e pagos, porém você não precisa informar nenhum dado de pagamento para começar seus estudos gratuitamente conosco. Os cursos gratuitos são completos e com certificado de conclusão, você não paga nada por eles.

    Porém, caso queira algo mais Premium , você terá acesso à diversos benefícios que vão te ajudar ainda mais em sua carreira.

  • Por onde devo começar?

    Siga SEMPRE as nossas Carreiras , elas vão te orientar em todos os sentidos. Os cursos já estão organizados em categorias e carreiras para facilitar seu aprendizado.
    Nossa sugestão para aprendizado é começar pelo Backend e seguindo para Frontend e Mobile.

    • Backend
    • Frontend
    • Mobile

  • Os cursos ensinam tudo que preciso?

    Nenhum curso no mundo vai te ensinar tudo, desculpa ser sincero! Os cursos são uma base, eles fornecem por volta de 30% do que você precisa aprender, o resto é com você, com dedicação e MUITA prática.

  • O que eu devo estudar?

    Java ou .NET? Angular ou React? Xamarin ou Flutter? A resposta é simples e direta: "Você já sabe o básico?"

    Se você ainda não sabe BEM o básico, ou seja, os fundamentos, OOP, SOLID, Clean Code, está perdendo tempo estudando Frameworks ou até coisas mais avançadas como Docker. Foque nos seus objetivos primeiro.
    Agora se você está indeciso sobre qual Framework estudar, a boa notícia é que o mercado neste momento está bem aquecido e você tem várias oportunidade. Desta forma o que levaríamos em conta para tomar esta decisão seria:

    • Já sei o básico
    • O Framework/Tecnologia tem mercado onde eu estou (região)
    • O Framework/Tecnologia é utilizado em uma empresa onde quero atual
    • O Framework/Tecnologia resolve meu problema
    • Eu gosto de utilizar o Framework/Tecnologia

  • Estou pronto para estudar no balta.io?

    Com certeza! O primeiro passo é começar e você pode fazer isto agora mesmo!

    Começar de graça

Ainda tem dúvidas?





Assine nosso Newsletter

Receba em primeira mão todas as nossas novidades.

Cadastrar


balta.io

Sobre

  • Como funciona?
  • Seja Premium
  • Agenda
  • Blog
  • Todos os cursos

Cursos

  • Frontend
  • Backend
  • Mobile
  • Fullstack

Suporte

  • Termos de uso
  • Privacidade
  • Cancelamento
  • Central de ajuda

Redes Sociais

  • Telegram
  • Facebook
  • Instagram
  • YouTube
  • Twitch
  • LinkedIn
  • Discord