Oktatóanyag: A tervezési szándék egyértelműbb kifejezése nullable és nem nullable referenciatípusokkal
A nullable reference types complement reference types ugyanúgy, mint a nullable value types complement value types . A változók nullázható hivatkozási típussá való deklarálásához ?
fűzze hozzá a változót a típushoz. Például egy string?
null értékű string
értéket jelöl. Ezekkel az új típusokkal pontosabban kifejezheti a tervezési szándékot: egyes változóknak mindig értéknek kell lenniük, másoknak pedig hiányzik egy érték.
Ebből az oktatóanyagból az alábbiakat sajátíthatja el:
- Nullable és non-nullable referenciatípusok beépítése a tervekbe
- Nullable reference type checks (Nullable reference type checks) engedélyezése a kódban.
- Írjon olyan kódot, amelyben a fordító kikényszeríti ezeket a tervezési döntéseket.
- A nullable reference funkció használata a saját terveiben
Előfeltételek
Be kell állítania a gépet a .NET futtatására, beleértve a C#-fordítót is. A C# fordító a Visual Studio 2022-ben vagy a .NET SDK-val érhető el.
Ez az oktatóanyag feltételezi, hogy ismeri a C# és a .NET használatát, beleértve a Visual Studiót vagy a .NET CLI-t is.
Nullable reference types in your designs
Ebben az oktatóanyagban egy felmérést futtató kódtárat fog létrehozni. A kód null értékű hivatkozástípusokat és nem nullértékű hivatkozástípusokat is használ a valós fogalmak megjelenítéséhez. A felmérésre vonatkozó kérdések soha nem lehetnek null értékűek. Előfordulhat, hogy egy válaszadó inkább nem válaszol egy kérdésre. A válaszok ebben az esetben lehetnek null
.
A mintához megírt kód kifejezi ezt a szándékot, és a fordító kényszeríti ezt a szándékot.
Az alkalmazás létrehozása és null értékű hivatkozástípusok engedélyezése
Hozzon létre egy új konzolalkalmazást a Visual Studióban vagy a parancssorból a használatával dotnet new console
. Nevezze el az alkalmazást NullableIntroduction
. Az alkalmazás létrehozása után meg kell adnia, hogy a teljes projekt egy engedélyezett null értékű jegyzetkörnyezetben legyen lefordítva. Nyissa meg a .csproj fájlt, és adjon hozzá egy Nullable
elemet az PropertyGroup
elemhez. Állítsa az értékét enable
értékűre. A C# 11-nél korábbi projektekben a nullázható referenciatípusok funkció mellett kell döntenie. Ennek az az oka, hogy a funkció bekapcsolása után a meglévő referenciaváltozó-deklarációk nem null értékű referenciatípusokká válnak. Bár ez a döntés segít megtalálni azokat a problémákat, amelyekben a meglévő kód nem rendelkezik megfelelő null-ellenőrzésekkel, előfordulhat, hogy nem tükrözi pontosan az eredeti tervezési szándékot:
<Nullable>enable</Nullable>
A .NET 6 előtt az új projektek nem tartalmazzák az Nullable
elemet. A .NET 6-tól kezdődően az új projektek tartalmazzák a <Nullable>enable</Nullable>
projektfájl elemét.
Az alkalmazás típusainak megtervezése
Ehhez a felmérési alkalmazáshoz több osztályt kell létrehozni:
- Egy osztály, amely modelleli a kérdések listáját.
- Egy osztály, amely modelleli a felméréshez kapcsolatba lépő személyek listáját.
- Egy osztály, amely egy felmérést végző személy válaszait modellezi.
Ezek a típusok null értékű és nem nullértékű hivatkozástípusokat is használnak annak megállapításához, hogy mely tagokra van szükség, és mely tagok választhatók. A nullértékű hivatkozástípusok egyértelműen közlik ezt a tervezési szándékot:
- A felmérés részét képező kérdések soha nem lehetnek null értékűek: Nincs értelme üres kérdést feltenni.
- A válaszadók soha nem lehetnek null értékűek. Érdemes nyomon követni azokat a személyeket, akikkel felvette a kapcsolatot, még a részvételt elutasító válaszadókat is.
- Egy kérdésre adott válasz null értékű lehet. A válaszadók elutasíthatják néhány vagy az összes kérdés megválaszolását.
Ha C# nyelven programozott, akkor lehet, hogy annyira megszokta, hogy olyan típusú hivatkozásokat használ, amelyek lehetővé teszik null
azokat az értékeket, amelyeket esetleg kihagyott más lehetőségekkel a nem null értékű példányok deklarálásához:
- A kérdések gyűjteményének nem lehet null értékű.
- A válaszadók gyűjteményének nem lehet null értékű.
A kód megírása során látni fogja, hogy a nem null értékű hivatkozási típusok a hivatkozások alapértelmezett típusaként elkerülik azokat a gyakori hibákat, amelyek s-hez vezethetnek NullReferenceException. Az oktatóanyag egyik tanulsága, hogy ön hozott döntéseket arról, hogy mely változók lehetnek vagy nem.null
A nyelv nem adott meg szintaxist ezeknek a döntéseknek a kifejezéséhez. Most már igen.
A létrehozni kívánt alkalmazás a következő lépéseket hajtja végre:
- Létrehoz egy felmérést, és kérdéseket ad hozzá.
- A felmérés válaszadóinak pszeudo-véletlenszerű halmazát hozza létre.
- A válaszadók mindaddig kapcsolatba lépnek a válaszadókkal, amíg a befejezett felmérés mérete el nem éri a célszámot.
- Fontos statisztikákat ír ki a felmérésre adott válaszokról.
A felmérés létrehozása nullable és nem nullable referenciatípusokkal
Az első megírni kívánt kód létrehozza a felmérést. Osztályokat fog írni egy felmérési kérdés modellezéséhez és egy felmérés futtatásához. A felmérés háromféle kérdést tartalmazhat, amelyeket a válasz formátuma különböztet meg: Igen/Nem válaszok, számválaszok és szöveges válaszok. Hozzon létre egy osztályt public SurveyQuestion
:
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
A fordító minden referenciatípus változódeklarációját nem null értékű referenciatípusként értelmezi a kódhoz egy engedélyezett nullértékű jegyzetkörnyezetben. Az első figyelmeztetést úgy tekintheti meg, ha tulajdonságokat ad hozzá a kérdés szövegéhez és a kérdés típusához, ahogy az a következő kódban látható:
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Mivel még nem inicializálta az inicializálást QuestionText
, a fordító figyelmeztetést ad ki arról, hogy a nem null értékű tulajdonság nincs inicializálva. A tervezéshez a kérdés szövegének nem null értékűnek kell lennie, ezért egy konstruktort is fel kell vennie az inicializáláshoz, valamint az QuestionType
értéket is. A kész osztálydefiníció a következő kódhoz hasonlóan néz ki:
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 konstruktor hozzáadása eltávolítja a figyelmeztetést. A konstruktor argumentum szintén nem null értékű hivatkozástípus, így a fordító nem ad ki figyelmeztetéseket.
Ezután hozzon létre egy nevű SurveyRun
osztálytpublic
. Ez az osztály az objektumok és metódusok listáját SurveyQuestion
tartalmazza a felméréshez kérdések hozzáadásához, az alábbi kódban látható módon:
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);
}
}
A korábbiakhoz hasonlóan a listaobjektumot nem null értékűre kell inicializálnia, vagy a fordító figyelmeztetést ad ki. A második túlterhelésben AddQuestion
nincsenek null értékű ellenőrzések, mert nincs rájuk szükség: A változót nem null értékűnek nyilvánította. Az értéke nem lehet null
.
Váltson a Program.cs fájlra a szerkesztőben, és cserélje le a tartalmát Main
a következő kódsorra:
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?");
Mivel a teljes projekt engedélyezve van a null értékű széljegyzetek környezetében, figyelmeztetések jelennek meg, amikor olyan metódusnak ad át null
, amely nem null értékű hivatkozástípust vár. Próbálja ki a következő sor hozzáadásával:Main
surveyRun.AddQuestion(QuestionType.Text, default);
Válaszadók létrehozása és válaszok kérése a felmérésre
Ezután írja meg azt a kódot, amely válaszokat hoz létre a felmérésre. Ez a folyamat több kisebb feladatot is magában foglal:
- Hozzon létre egy metódust, amely válaszadó objektumokat hoz létre. Ezek a felmérés kitöltésére felkért személyeket jelölik.
- A logikát úgy hozhatja létre, hogy szimulálja a válaszadóknak feltett kérdéseket, és válaszokat gyűjtsen, vagy észrevehesse, hogy a válaszadó nem válaszolt.
- Ismételje meg a műveletet, amíg elegendő válaszadó nem válaszolt a felmérésre.
Szüksége lesz egy osztályra a felmérésre adott válasz megjelenítéséhez, ezért most adja hozzá. Null értékű támogatás engedélyezése. Adjon hozzá egy tulajdonságot Id
és egy konstruktort, amely inicializálja azt az alábbi kódban látható módon:
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
public SurveyResponse(int id) => Id = id;
}
}
Ezután adjon hozzá egy metódust static
, amellyel új résztvevőket hozhat létre egy véletlenszerű azonosító létrehozásával:
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
Ennek az osztálynak a fő feladata, hogy a felmérésben szereplő kérdésekre adott válaszokat generálja a résztvevőknek. Ennek a felelősségnek néhány lépése van:
- Kérjen részvételt a felmérésben. Ha a személy nem járul hozzá, hiányzó (vagy null) választ ad vissza.
- Tegyen fel minden kérdést, és jegyezze fel a választ. Előfordulhat, hogy minden válasz hiányzik (vagy null).
Adja hozzá a következő kódot az SurveyResponse
osztályhoz:
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!";
}
}
A felmérésre adott válaszok tárolója egy Dictionary<int, string>?
, amely azt jelzi, hogy null értékű lehet. Az új nyelvi funkcióval deklarálja a tervezési szándékot mind a fordítónak, mind a kód későbbi olvasóinak. Ha az érték ellenőrzése nélkül halasztja el a null
dedukciótsurveyResponses
, egy fordítói figyelmeztetést kap. Nem kap figyelmeztetést a AnswerSurvey
metódusban, mert a fordító meg tudja állapítani, hogy a surveyResponses
változó a fenti nem null értékre lett állítva.
A hiányzó válaszok használata null
kiemeli a nullértékű hivatkozástípusok használatának egyik kulcspontját: nem az a cél, hogy az összes null
értéket eltávolítsa a programból. Ehelyett az a cél, hogy a megírt kód kifejezze a terv szándékát. A hiányzó értékek a kódban való kifejezéshez szükségesek. Az null
érték egyértelmű módot ad a hiányzó értékek kifejezésére. Az összes null
érték eltávolításának megkísérlése csak a hiányzó értékek kifejezésének más módon történő definiálásához vezet a nélkül null
.
Ezután meg kell írnia a metódust PerformSurvey
a SurveyRun
osztályban. Adja hozzá a következő kódot a SurveyRun
osztályhoz:
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);
}
}
Itt is a null értékű List<SurveyResponse>?
válasz azt jelzi, hogy a válasz null értékű lehet. Ez azt jelzi, hogy a felmérést még nem adták meg a válaszadóknak. Figyelje meg, hogy a válaszadók mindaddig hozzáadódnak, amíg elegendően nem járultak hozzá.
A felmérés futtatásának utolsó lépése egy hívás hozzáadása a felmérés végrehajtásához a Main
metódus végén:
surveyRun.PerformSurvey(50);
Felmérésre adott válaszok vizsgálata
Az utolsó lépés a felmérés eredményeinek megjelenítése. Kódot fog hozzáadni számos megírt osztályhoz. Ez a kód bemutatja a nullable és a nem nullable referenciatípusok megkülönböztetésének értékét. Először adja hozzá a következő két kifejezéstestű tagot az SurveyResponse
osztályhoz:
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Mivel surveyResponses
a hivatkozás null értékű, a hivatkozás megszüntetése előtt null értékű ellenőrzésekre van szükség. A Answer
metódus egy nem null értékű sztringet ad vissza, ezért a null-coalescing operátorral kell lefednünk a hiányzó válasz esetét.
Ezután adja hozzá ezt a három kifejezéstestű tagot az SurveyRun
osztályhoz:
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
A AllParticipants
tagnak figyelembe kell vennie, hogy a respondents
változó lehet null, de a visszatérési érték nem lehet null. Ha a kifejezést a és az ??
azt követő üres sorozat eltávolításával módosítja, a fordító figyelmezteti, hogy a metódus visszatérhet null
, a visszatérési aláírása pedig nem null értékű típust ad vissza.
Végül adja hozzá a következő hurkot a Main
metódus alján:
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");
}
}
Nincs szükség ellenőrzésekre null
ebben a kódban, mert úgy tervezte meg a mögöttes interfészeket, hogy azok mindegyike ne null értékű hivatkozástípusokat ad vissza.
A kód letöltése
A kész oktatóanyag kódját a csharp/NullableIntroduction mappában található mintaadattárból szerezheti be.
Kísérletezzen a típusdeklarációk nullable és nem nullable referenciatípusok közötti módosításával. Tekintse meg, hogy ez hogyan generál különböző figyelmeztetéseket, hogy biztosan ne halasztsa el véletlenül a -t null
.
Következő lépések
Megtudhatja, hogyan használhat null értékű hivatkozástípust az Entity Framework használatakor:
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: