Compartilhar via


Atualizar uma base de código com tipos de referência anuláveis para melhorar os avisos de diagnóstico nulos

Tipos de referência anuláveis permitem declarar se variáveis de um tipo de referência devem ou não ser atribuídas a um valor null. A análise estática e os avisos do compilador quando seu código pode desreferenciar null são o benefício mais importante desse recurso. Depois de habilitado, o compilador gera avisos que ajudam você a evitar a geração de System.NullReferenceException quando o código é executado.

Se a base de código for relativamente pequena, você poderá ativar o recurso em seu projeto, endereçar avisos e aproveitar os benefícios do diagnóstico aprimorado. Bases de código maiores podem exigir uma abordagem mais estruturada para endereçar avisos ao longo do tempo, habilitando o recurso para alguns, conforme você aborda avisos em diferentes tipos ou arquivos. Este artigo descreve diferentes estratégias para atualizar uma base de código e as compensações associadas a essas estratégias. Antes de iniciar a migração, leia a visão geral conceitual dos tipos de referência anuláveis. Ele aborda a análise estática do compilador, valores do estado de nulo iguais a talvez nulo e não nulo e anotações anuláveis. Depois de conhecer esses conceitos e termos, você estará pronto para migrar seu código.

Planeje sua migração

Independentemente de como você atualiza sua base de código, a meta é que avisos anuláveis e anotações anuláveis sejam ativados em seu projeto. Depois de atingir essa meta, você terá a configuração <nullable>Enable</nullable> em seu projeto. Você não precisará de nenhuma das diretivas de pré-processador para ajustar as configurações em outro lugar.

Observação

Você pode designar uma configuração Nullable para seu projeto usando uma marca <Nullable>. Confira Opções do compilador para obter mais informações.

A primeira opção é definir o padrão do projeto. Suas opções são:

  1. Nullable disable como padrão: disable é o padrão se você não adicionar um elemento Nullable ao arquivo de projeto. Use esse padrão quando não estiver ativamente adicionando novos arquivos à base de código. A atividade principal é atualizar a biblioteca para usar tipos de referência anuláveis. Usar esse padrão significa que você adiciona uma diretiva de pré-processador anulável a cada arquivo à medida que atualiza o código deles.
  2. Nullabilidade habilitada como padrão: configure este padrão quando estiver desenvolvendo ativamente novos recursos. Você deseja que todo novo código se beneficie de tipos de referência anulável e análise estática de anulabilidade. Usar esse padrão significa que você deve adicionar #nullable disable à parte superior de cada arquivo. Você removerá essas diretivas de pré-processador ao resolver os avisos em cada arquivo.
  3. Avisos anuláveis como padrão: escolha esse padrão para uma migração em duas fases. Na primeira fase, aborde os alertas. Na segunda fase, ative as anotações para declarar o estado de nulo esperado de uma variável. Usar esse padrão significa que você deve adicionar #nullable disable à parte superior de cada arquivo.
  4. Anotações anuláveis como padrão. Faça anotações no código antes de abordar os avisos.

Habilitar "nulo" como padrão aumenta o trabalho inicial para adicionar as diretivas de pré-processador a cada arquivo. A vantagem é que cada novo arquivo de código adicionado ao projeto terá nulidade habilitada. Qualquer novo trabalho será habilitado para anulável; somente o código existente deve ser atualizado. Desabilitar nulo como padrão funciona melhor se a biblioteca estiver estável e o foco principal do desenvolvimento for a adoção de tipos de referência nulos. Você ativa tipos de referência anuláveis à medida que anota as APIs. Ao terminar, você irá habilitar os tipos de referência anuláveis para todo o projeto. Ao criar um novo arquivo, você deve adicionar as diretivas de pré-processador e torná-lo ciente de nulabilidade. Se algum desenvolvedor em sua equipe se esquecer, esse novo código será adicionado ao backlog das tarefas para tornar todo o código compatível com nulidade.

A escolha entre essas estratégias depende do quanto de desenvolvimento ativo está ocorrendo em seu projeto. Quanto mais maduro e estável o projeto, melhor será a segunda estratégia. Quanto mais recursos sendo desenvolvidos, melhor será a primeira estratégia.

Importante

O contexto global anulável não se aplica aos arquivos de código gerados. Em qualquer das estratégias, o contexto anulável é desabilitado para todo arquivo de origem marcado como gerado. Isso significa que todas as APIs em arquivos gerados não são anotadas. Há quatro maneiras de um arquivo ser marcado como gerado:

  1. No .editorconfig, especifique generated_code = true em uma seção que se aplica a esse arquivo.
  2. Coloque <auto-generated> ou <auto-generated/> em um comentário na parte superior do arquivo. Ele pode estar em qualquer linha nesse comentário, mas o bloco de comentários deve ser o primeiro elemento do arquivo.
  3. Inicie o nome do arquivo com TemporaryGeneratedFile_
  4. Termine o nome do arquivo com .designer.cs, .generated.cs, .g.cs ou .g.i.cs.

Os geradores podem aceitar usando a diretiva de pré-processador #nullable.

Entender contextos e avisos

Habilitando avisos e anotações controla a forma como o compilador entende tipos de referência e anulabilidade. Cada tipo tem uma entre três nulidades:

  • oblivious: todos os tipos de referência são nullable oblivious quando o contexto de anotação está desabilitado.
  • nonnullable: um tipo de referência não anotado, C é nonnullable quando o contexto de anotação está habilitado.
  • nullable: um tipo de referência anotado, C?, é nullable, mas um aviso pode ser emitido quando o contexto de anotação está desabilitado. As variáveis declaradas com var são nullable quando o contexto de anotação está habilitado.

O compilador gera avisos com base nessa nulidade:

  • Tipos nonnullable geram avisos se um valor potencial null é atribuído a eles.
  • Tipos nullable geram avisos se forem desreferenciados quando maybe-null.
  • Tipos oblivious geram avisos se são desreferenciados quando talvez nulo e o contexto de aviso está habilitado.

Cada variável tem um estado anulável padrão que depende de sua nulidade:

  • As variáveis nullable têm um estado de nulo padrão igual a talvez nulo.
  • As variáveis non-nullable têm um estado de nulo padrão igual a não nulo.
  • As variáveis nullable oblivious têm um estado de nulo padrão igual a não nulo.

Antes de habilitar tipos de referência anuláveis, todas as declarações da sua base de código são nullable oblivious. Isso é importante porque significa que todos os tipos de referência têm um estado de nulo padrão igual a não nulo.

Abordar avisos

Se o projeto usa o Entity Framework Core, você deve ler as diretrizes dele sobre Como trabalhar com tipos de referência anuláveis.

Ao iniciar a migração, você deve começar habilitando apenas avisos. Todas as declarações permanecem nullable oblivious, mas você verá avisos quando desreferenciar um valor após o estado de nulo dele mudar para talvez nulo. Ao abordar esses avisos, você verificará a presença de valores nulos em mais pontos, tornando sua base de código mais resiliente. Para aprender técnicas específicas adequadas a diferentes situações, confira o artigo sobre Técnicas para resolver avisos anuláveis.

Você pode resolver os avisos e habilitar anotações em cada arquivo ou classe antes de prosseguir com o restante do código. No entanto, geralmente é mais eficiente resolver os avisos gerados enquanto o contexto é avisos, antes de habilitar as anotações de tipo. Assim, todos os tipos são ignorados até que você resolva o primeiro conjunto de avisos.

Habilitar anotações de tipo

Depois de abordar o primeiro conjunto de avisos, você pode habilitar o contexto de anotação. Isso altera os tipos de referência de oblivious para nonnullable. Todas as variáveis declaradas com var são anuláveis. Essa alteração geralmente introduz novos avisos. A primeira etapa para abordar os avisos do compilador é usar anotações ? em tipos de retorno e parâmetro para indicar quando os argumentos ou os valores retornados podem ser null. Ao realizar essa tarefa, sua meta não é apenas corrigir avisos. A meta mais importante é fazer com que o compilador entenda sua intenção com os possíveis valores nulos.

Atributos estendem anotações de tipo

Vários atributos foram adicionados para expressar informações adicionais sobre o estado de nulo das variáveis. As regras das suas APIs provavelmente são mais complicadas do que não nulo ou possivelmente nulo para todos os parâmetros e valores retornados. Muitas das suas APIs têm regras mais complexas para quando as variáveis podem ou não ser null. Nesses casos, você usará atributos para expressar essas regras. Os atributos que descrevem a semântica da sua API são encontrados no artigo sobre Atributos que afetam a análise anulável.

Próximas etapas

Depois de resolver todos os avisos após habilitar anotações, você pode definir o contexto padrão do seu projeto como habilitado. Se você adicionou pragmas ao seu código para o contexto de anotações ou avisos anuláveis, você pode removê-los. Com o tempo, você pode ver novos avisos. Você pode escrever um código que introduz avisos. Uma dependência de biblioteca pode ser atualizada referente aos tipos de referência anuláveis. Essas atualizações vão alterar os tipos dessa biblioteca de nullable oblivious para nonnullable ou nullable.

Você também pode explorar esses conceitos no módulo do Learn sobre Segurança de anuláveis em C#.