Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Tip
A começar um novo projeto? Novos projetos criados a partir de .NET 6 ou modelos posteriores já têm <Nullable>enable</Nullable> definido. Não precisas de uma estratégia de migração: salta para Resolver avisos anuláveis.
Manter uma base de código existente? Leia primeiro os tipos de referência anuláveis para compreender contextos, anotações e estado nulo. Este artigo parte do princípio que está familiarizado com esses conceitos e pronto para planear um lançamento.
Quando ativa os tipos de referência anuláveis num projeto grande que começou antes da introdução dos tipos de referência anuláveis, o compilador produz muitos avisos ao mesmo tempo. A migração consiste em sequenciar o trabalho: escolher um contexto padrão, expor avisos ficheiro a ficheiro ou secção a secção, e convergir para <Nullable>enable</Nullable> todo o projeto. A sequência correta depende de quão ativa é a base de código e do risco que consegues correr numa única passada.
O estado final é o mesmo em todos os casos: o projeto define <Nullable>enable</Nullable> e não contém diretivas de pré-processador #nullable.
Escolha um contexto padrão
O contexto anulável tem duas bandeiras independentes: anotações (se ? declara um tipo de referência anulável) e avisos (se o compilador emite diagnósticos). Junte-os como um único <Nullable> valor:
| Valor predefinido | Anotações | Avisos | Melhor para |
|---|---|---|---|
disable
(implícito) |
off | off | Bibliotecas estáveis que não aceitam novas funcionalidades trabalham nesta fase. |
enable |
on | on | Bases de código ativas com ficheiros novos frequentes. O novo código vem ativado por predefinição. |
warnings |
off | on | Migração em duas fases: abordar primeiro os avisos, anotar depois. |
annotations |
on | off | Anote a API pública antes de corrigir os avisos internos. |
Escolha a estratégia que melhor se adequa aos objetivos da migração do seu projeto:
- Desativar como padrão. Define
<Nullable>disable</Nullable>e adiciona#nullable enableno topo de cada ficheiro à medida que o migras. Os ficheiros existentes permanecem anuláveis — alheios até tocares neles. Esta opção tem a menor fricção para bibliotecas estáveis porque o trabalho de novas funcionalidades é raro. - Ativar como padrão. Define
<Nullable>enable</Nullable>e adiciona#nullable disableno topo de cada ficheiro que ainda não migraste. Cada novo ficheiro é consciente do nullable desde o início, pelo que o backlog de migração só pode diminuir. Esta escolha é melhor quando o desenvolvimento está ativo. - Avisos como padrão. Defina
<Nullable>warnings</Nullable>. Escolha este padrão para uma migração em duas fases: enderece avisos enquanto todos os tipos de referência ainda são tratados como alheios, depois ativar as anotações. A divisão em duas fases mantém o diff de cada passo bem delimitado. - Anotações por predefinição. Defina
<Nullable>annotations</Nullable>. Comece por anotar a sua API pública (?nos membros que permitemnull) antes de andar atrás dos avisos. O compilador ainda não emite avisos, por isso podes estabilizar a superfície da API sem distrações.
O teu ficheiro de projeto controla o padrão global.
#nullable
As diretivas do pré-processador sobrepõem-se a esse padrão para uma região de código:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
Dentro dos ficheiros fonte, a diretiva opta por uma região dentro ou fora da definição anulável do projeto:
#nullable disable
public static class LegacyHelper
{
// This file is nullable-oblivious. Reference types use the legacy rules.
public static string GetGreeting(string name) =>
name == null ? "hello" : $"hello {name}";
}
#nullable restore
#nullable enable
public static class MigratedHelper
{
// This file is fully migrated. Reference types are non-nullable by default.
public static string GetGreeting(string? name) =>
name is null ? "hello" : $"hello {name}";
}
#nullable restore
Migrar ficheiro a ficheiro
A forma mais previsível de migrar um projeto grande é ativar avisos ou anotações ficheiro a ficheiro. O padrão é o mesmo independentemente da predefinição que escolher:
- Escolha um arquivo. Começa pelos tipos de folhas mais profundos no teu gráfico de dependência, depois avança para fora. Anotar um tipo provoca novos avisos nos seus chamadores, pelo que trabalhar de baixo para cima minimiza a reestruturação.
- Adicione a diretiva
#nullableque ativa o novo comportamento para o ficheiro. Usa#nullable enablese quiseres ambas as bandeiras. Utilize#nullable enable warningsexclusivamente para advertências. - Resolva os avisos no ficheiro utilizando as técnicas descritas em Resolver avisos anuláveis.
- Repete para o próximo ficheiro.
- Quando cada ficheiro do projeto tiver a sua diretiva, remova as diretivas e defina
<Nullable>enable</Nullable>ao nível do projeto.
Se a tua base de código já tem <Nullable>enable</Nullable>, vais na direção oposta. Suprime avisos em ficheiros não migrados até estares pronto. Utilize #nullable disable para excluir ficheiros e, em seguida, remova as supressões uma a uma.
Migrar em duas fases
Uma migração em duas fases separa os dois tipos de trabalho que os tipos de referência anuláveis envolvem. Podes sequenciar as fases de qualquer forma, dependendo de qual forma de estabilidade é mais importante para ti.
Primeiro os avisos, depois as anotações
A prioridade é começar com avisos ao corrigir bugs System.NullReferenceException latentes:
- Fase 1: Abordar os avisos. Defina a predefinição do projeto como
warnings. Os tipos de referência continuam sem ter em conta a nulabilidade, pelo que o sistema de tipos ainda não sofre alterações. O compilador emite avisos em todos os locais onde o seu código existente possa já lançar um System.NullReferenceException. Adicionar verificações de nulo, reestruturar o fluxo ou aplicar atributos até que o projeto fique sem avisos. Cada correção torna o código de produção mais resiliente mesmo antes de existirem anotações. - Fase 2: Adicionar anotações. Altere a predefinição do projeto para
enable. Os tipos de referência são agora não anuláveis por defeito, evaros locais tornam-se nulos. Novos avisos refletem declarações que não correspondem à forma como as variáveis são usadas. Adicione?a tipos que deverão permitirnull. Restringir as APIs que devem exigir parâmetros não nulos.
Primeiro as anotações, depois os avisos
Dê prioridade às anotações quando a prioridade for estabilizar a superfície pública da API. Esta sequência é adequada às bibliotecas: pode enviar assinaturas anotadas para que os consumidores vejam os contratos corretos, e depois encerrar os avisos internos ao seu próprio ritmo.
- Fase 1: Adicionar anotações. Definir a predefinição do projeto como
annotations. Os tipos de referência tornam-se não anuláveis por predefinição, mas o compilador não emite avisos, pelo que evita ruído desnecessário. Percorra a API pública e adicione?a cada membro que possa legitimamente regressar ou aceitarnull. Aperte as assinaturas que não devia apertar. Como os avisos estão desligados, podes ajustar a forma da API em commits focados sem desembaraçar a implementação ao mesmo tempo. - Fase 2: Abordar os avisos. Altera o projeto predefinido para
enable. As anotações que adicionaste na fase 1 agora alimentam análise de estado nulo, por isso os avisos que o compilador emite são de melhor qualidade desde o início: cada um aponta para código cujo comportamento não corresponde ao contrato que já publicaste. Resolva-os com as técnicas descritas em Resolver avisos de anulabilidade.
Escolher entre as ordenações
Cada ordenação separa as fases em diferenças mais pequenas e mais fáceis de rever. Uma fase muda apenas o comportamento, e a outra muda apenas os tipos. A desvantagem é que se visita cada ficheiro duas vezes. Para código maduro e estável, onde cada alteração implica risco, as duas passagens geralmente valem a pena. Escolhe os avisos primeiro quando mais quiseres reforçar o código em execução. Escolhe as anotações primeiro quando mais quiseres publicar um contrato estável.
O código gerado é excluído
O compilador trata ficheiros marcados como gerados como se o contexto anulável estivesse desativado, independentemente da definição do projeto. Um ficheiro é considerado gerado quando qualquer uma das seguintes condições é verdadeira:
- Uma
.editorconfigregra definegenerated_code = truepara o ficheiro. - O primeiro comentário no ficheiro contém
<auto-generated>ou<auto-generated/>. - O nome do ficheiro começa por
TemporaryGeneratedFile_. - O nome do ficheiro termina em
.designer.cs,.generated.cs,.g.cs, ou.g.i.cs.
Geradores que produzem saída consciente do nullable podem optar por voltar a emitir #nullable enable no topo do ficheiro gerado.
Quando terminares
Depois de todos os ficheiros participarem no projeto predefinido e o elemento <Nullable>enable</Nullable> estar definido:
- Remova todas as diretivas
#nullabledo seu código-fonte. - Remove os inicializadores
null!edefault!que adicionaste apenas para silenciar os avisos durante a migração. Substitui-os por uma inicialização adequada, ou faça do tipo um tipo de referência anulável. - Faça uma verificação pontual da API pública. Cada membro que regressar ou aceitar
nulldeve ser anotado com?. As anotações fazem parte do teu contrato assim que o pacote for enviado.
Agora estás no mesmo estado dos novos projetos: os tipos de referência anuláveis fazem parte do sistema de tipos, e quaisquer novos avisos refletem uma verdadeira incompatibilidade entre declarações e código. Use Resolver avisos de anulabilidade para tratá-los à medida que forem surgindo.