Eventos
Crie aplicativos e agentes de IA
17 de mar., 21 - 21 de mar., 10
Junte-se à série de encontros para criar soluções de IA escaláveis com base em casos de uso do mundo real com outros desenvolvedores e especialistas.
Registrar agoraNão há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
Tipos de referência anuláveis complementam tipos de referência da mesma forma que tipos de valor anuláveis complementam tipos de valor. Você declara uma variável como um tipo de referência anulável acrescentando um ?
ao tipo. Por exemplo, string?
representa um string
anulável. Você pode usar esses novos tipos para expressar mais claramente sua intenção de design: algumas variáveis sempre devem ter um valor, outras podem estar perdendo um valor.
Neste tutorial, você aprenderá a:
Você precisará configurar seu computador para executar o .NET, incluindo o compilador C#. O compilador C# está disponível com do Visual Studio 2022 ou o SDK do .NET .
Este tutorial pressupõe que você esteja familiarizado com C# e .NET, incluindo o Visual Studio ou a CLI do .NET.
Neste tutorial, você criará uma biblioteca que modela a execução de uma pesquisa. O código usa tipos de referência anuláveis e tipos de referência não anuláveis para representar os conceitos do mundo real. As perguntas da pesquisa nunca podem ser nulas. Um entrevistado pode preferir não responder a uma pergunta. As respostas podem ser null
nesse caso.
O código que você escreverá para este exemplo expressa essa intenção e o compilador impõe essa intenção.
Crie um novo aplicativo de console no Visual Studio ou na linha de comando usando dotnet new console
. Nomeie o aplicativo NullableIntroduction
. Depois de criar o aplicativo, você precisará especificar que todo o projeto é compilado em um contexto de anotação anulável habilitado. Abra o arquivo .csproj e adicione um elemento Nullable
ao elemento PropertyGroup
. Defina seu valor como enable
. Você precisa aceitar o recurso de tipos de referência que permitem valor nulo em projetos anteriores ao C# 11. Isso ocorre porque, depois que o recurso é ativado, as declarações de variável de referência existentes se tornam tipos de referência não anuláveis. Embora essa decisão ajude a encontrar problemas em que o código existente pode não ter verificações nulas adequadas, ela pode não refletir com precisão sua intenção de design original:
<Nullable>enable</Nullable>
Antes do .NET 6, novos projetos não incluem o elemento Nullable
. A partir do .NET 6, novos projetos incluem o elemento <Nullable>enable</Nullable>
no arquivo de projeto.
Este aplicativo de pesquisa requer a criação de várias classes:
Esses tipos usarão tipos de referência anuláveis e não anuláveis para expressar quais membros são necessários e quais membros são opcionais. Tipos de referência anuláveis comunicam claramente essa intenção de design:
Se você programou em C#, pode estar tão acostumado a tipos de referência que permitem valores null
que talvez não tenha percebido outras oportunidades para declarar variáveis como não anuláveis.
Ao escrever o código, você verá que um tipo de referência não anulável como o padrão para referências evita erros comuns que podem levar a NullReferenceExceptions. Uma lição deste tutorial é que você tomou decisões sobre quais variáveis poderiam ou não ser null
. A linguagem não forneceu sintaxe para expressar essas decisões. Agora ele já fornece.
O aplicativo que você criará executa as seguintes etapas:
O primeiro código que você escreverá cria a pesquisa. Você escreverá classes para modelar uma pergunta da pesquisa e uma execução da pesquisa. Sua pesquisa tem três tipos de perguntas, distinguidas pelo formato da resposta: respostas sim/não, respostas numéricas e respostas de texto. Crie uma classe public SurveyQuestion
:
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
O compilador interpreta cada declaração de variável de tipo de referência como um tipo de referência não anulável para o código em um contexto de anotação anulável habilitado. Você pode ver seu primeiro aviso adicionando propriedades para o texto da pergunta e o tipo de pergunta, conforme mostrado no código a seguir:
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Como você não inicializou QuestionText
, o compilador emite um aviso de que uma propriedade não anulável não foi inicializada. Seu design exige que o texto da pergunta seja não nulo, portanto, você adiciona um construtor para inicializá-lo e o valor QuestionType
também. A definição de classe concluída se parece com o seguinte código:
namespace NullableIntroduction;
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
public SurveyQuestion(QuestionType typeOfQuestion, string text) =>
(TypeOfQuestion, QuestionText) = (typeOfQuestion, text);
}
A adição do construtor removerá o aviso. O argumento construtor também é um tipo de referência não anulável, portanto, o compilador não emite avisos.
Em seguida, crie uma classe de public
chamada SurveyRun
. Essa classe contém uma lista de SurveyQuestion
objetos e métodos para adicionar perguntas à pesquisa, conforme mostrado no código a seguir:
using System.Collections.Generic;
namespace NullableIntroduction
{
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = new List<SurveyQuestion>();
public void AddQuestion(QuestionType type, string question) =>
AddQuestion(new SurveyQuestion(type, question));
public void AddQuestion(SurveyQuestion surveyQuestion) => surveyQuestions.Add(surveyQuestion);
}
}
Como antes, você deve inicializar o objeto de lista para um valor não nulo ou o compilador emite um aviso. Não há verificações nulas na segunda sobrecarga de AddQuestion
porque elas não são necessárias: você declarou essa variável como não anulável. Seu valor não pode ser null
.
Alterne para Program.cs em seu editor e substitua o conteúdo de Main
pelas seguintes linhas de código:
var surveyRun = new SurveyRun();
surveyRun.AddQuestion(QuestionType.YesNo, "Has your code ever thrown a NullReferenceException?");
surveyRun.AddQuestion(new SurveyQuestion(QuestionType.Number, "How many times (to the nearest 100) has that happened?"));
surveyRun.AddQuestion(QuestionType.Text, "What is your favorite color?");
Como o projeto inteiro está em um contexto de anotação anulável habilitado, você receberá avisos quando passar null
para qualquer método que espera um tipo de referência não anulável. Experimente adicionando a seguinte linha a Main
:
surveyRun.AddQuestion(QuestionType.Text, default);
Em seguida, escreva o código que gera respostas para a pesquisa. Esse processo envolve várias tarefas pequenas:
Você precisará de uma classe para representar uma resposta de pesquisa, então adicione-a agora. Habilitar o suporte para tipos que permitem valor nulo. Adicione uma propriedade Id
e um construtor que a inicialize, conforme mostrado no seguinte código:
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
public SurveyResponse(int id) => Id = id;
}
}
Em seguida, adicione um método static
para criar novos participantes gerando uma ID aleatória:
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
A principal responsabilidade dessa classe é gerar as respostas de um participante às perguntas da pesquisa. Essa responsabilidade tem algumas etapas:
Adicione o seguinte código à classe SurveyResponse
:
private Dictionary<int, string>? surveyResponses;
public bool AnswerSurvey(IEnumerable<SurveyQuestion> questions)
{
if (ConsentToSurvey())
{
surveyResponses = new Dictionary<int, string>();
int index = 0;
foreach (var question in questions)
{
var answer = GenerateAnswer(question);
if (answer != null)
{
surveyResponses.Add(index, answer);
}
index++;
}
}
return surveyResponses != null;
}
private bool ConsentToSurvey() => randomGenerator.Next(0, 2) == 1;
private string? GenerateAnswer(SurveyQuestion question)
{
switch (question.TypeOfQuestion)
{
case QuestionType.YesNo:
int n = randomGenerator.Next(-1, 2);
return (n == -1) ? default : (n == 0) ? "No" : "Yes";
case QuestionType.Number:
n = randomGenerator.Next(-30, 101);
return (n < 0) ? default : n.ToString();
case QuestionType.Text:
default:
switch (randomGenerator.Next(0, 5))
{
case 0:
return default;
case 1:
return "Red";
case 2:
return "Green";
case 3:
return "Blue";
}
return "Red. No, Green. Wait.. Blue... AAARGGGGGHHH!";
}
}
O armazenamento das respostas da pesquisa é um Dictionary<int, string>?
, indicando que ele pode ser um valor nulo. Você está usando o novo recurso de linguagem para declarar sua intenção de design, tanto para o compilador quanto para qualquer pessoa que leia seu código mais tarde. Se você desreferenciar surveyResponses
sem verificar o valor do null
primeiro, receberá um aviso do compilador. Você não recebe um aviso no método AnswerSurvey
porque o compilador pode determinar que a variável surveyResponses
foi definida como um valor não nulo acima.
Usar null
para respostas ausentes realça um ponto-chave para trabalhar com tipos de referência anuláveis: sua meta não é remover todos os valores null
do seu programa. Em vez disso, seu objetivo é garantir que o código que você escreve expresse a intenção do seu design. Valores ausentes são um conceito necessário para expressar em seu código. O valor null
é uma maneira clara de expressar esses valores ausentes. Tentar remover todos os valores null
leva apenas a definir alguma outra maneira de expressar esses valores ausentes sem null
.
Em seguida, você precisa escrever o método PerformSurvey
na classe SurveyRun
. Adicione o seguinte código na classe SurveyRun
:
private List<SurveyResponse>? respondents;
public void PerformSurvey(int numberOfRespondents)
{
int respondentsConsenting = 0;
respondents = new List<SurveyResponse>();
while (respondentsConsenting < numberOfRespondents)
{
var respondent = SurveyResponse.GetRandomId();
if (respondent.AnswerSurvey(surveyQuestions))
respondentsConsenting++;
respondents.Add(respondent);
}
}
Aqui novamente, sua escolha de um List<SurveyResponse>?
anulável indica que a resposta pode ser nula. Isso indica que a pesquisa ainda não foi dada a nenhum entrevistado. Observe que os entrevistados são adicionados até que um número suficiente tenha consentido.
A última etapa para executar a pesquisa é adicionar uma chamada para executar a pesquisa no final do método Main
:
surveyRun.PerformSurvey(50);
A última etapa é exibir os resultados da pesquisa. Você adicionará código a muitas das classes que você escreveu. Esse código demonstra o valor da distinção de tipos de referência anuláveis e não anuláveis. Comece adicionando os dois membros com corpo de expressão à classe SurveyResponse
:
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Como surveyResponses
é um tipo de referência anulável, são necessárias verificações de nulidade antes de desreferenciá-la. O método Answer
retorna uma string não anulável, portanto, temos que cobrir o caso de uma resposta ausente usando o operador de coalescência nula.
Em seguida, adicione esses três membros com corpo de expressão à classe SurveyRun
:
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
O membro AllParticipants
deve levar em conta que a variável respondents
pode ser nula, mas o valor retornado não pode ser nulo. Se você alterar essa expressão removendo o ??
e a sequência vazia a seguir, o compilador avisará que o método pode retornar null
e sua assinatura de retorno retornará um tipo não anulável.
Por fim, adicione o seguinte loop na parte inferior do método Main
:
foreach (var participant in surveyRun.AllParticipants)
{
Console.WriteLine($"Participant: {participant.Id}:");
if (participant.AnsweredSurvey)
{
for (int i = 0; i < surveyRun.Questions.Count; i++)
{
var answer = participant.Answer(i);
Console.WriteLine($"\t{surveyRun.GetQuestion(i).QuestionText} : {answer}");
}
}
else
{
Console.WriteLine("\tNo responses");
}
}
Você não precisa de nenhuma verificação de null
neste código porque criou as interfaces subjacentes para que todas elas retornem tipos de referência não anuláveis.
Obtenha o código do tutorial concluído em nosso repositório de amostras na pasta csharp/NullableIntroduction.
Experimente alterando as declarações de tipo entre tipos de referência anuláveis e não anuláveis. Veja como isso gera avisos diferentes para garantir que um null
não será acidentalmente cancelado.
Saiba como usar o tipo de referência anulável ao usar o Entity Framework:
Comentários do .NET
O .NET é um projeto código aberto. Selecione um link para fornecer comentários:
Eventos
Crie aplicativos e agentes de IA
17 de mar., 21 - 21 de mar., 10
Junte-se à série de encontros para criar soluções de IA escaláveis com base em casos de uso do mundo real com outros desenvolvedores e especialistas.
Registrar agoraTreinamento
Módulo
Segurança nula em C# - Training
Aprenda as práticas de codificação para ajudar a evitar a ocorrência de NullReferenceException.
Documentação
Tipos de referência anuláveis - C#
Este artigo fornece uma visão geral dos tipos de referência que permitem valor nulo. Saiba como o recurso oferece segurança contra exceções de referência nula, para projetos novos e existentes.
Atualizar sua base de código para usar tipos de referência anuláveis - C#
Saiba mais sobre contextos anuláveis, como habilitar ou desabilitar avisos e anotações e como diagnosticar e corrigir possíveis problemas de desreferenciamento nulo.
Tutorial de instruções de nível superior - C#
Este tutorial mostra como você pode usar instruções de nível superior para experimentar e provar conceitos ao explorar suas ideias