Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Null értékű hivatkozástípusok ugyanúgy kiegészítik a hivatkozástípusokat, mint a null értékű értéktípusok. Egy változót null értékű hivatkozástípusnak deklarálhat, ha hozzáfűz egy ? a típushoz. A string? például null értékű stringjelö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íg másoknak hiányzik egy érték.
Ebben az oktatóanyagban a következőket sajátíthatja el:
- Nullálható és nem nullálható referenciatípusok beépítése a tervekbe.
- Engedélyezze a null értékű hivatkozástípus-ellenőrzéseket a teljes kódbázisban.
- Írjon olyan kódot, amelyben a fordító kikényszeríti ezeket a tervezési döntéseket.
- Használja a null értékű referencia funkciót saját terveiben.
Előfeltételek
- A legújabb .NET SDK
- Visual Studio Code szerkesztő
- A C# DevKit
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.
Opcionális referenciatípusok beépítése a tervekbe
Ebben az oktatóanyagban egy felmérést modellező könyvtá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. Ebben az esetben előfordulhat, hogy a válaszok null.
A mintához írt kód kifejezi ezt a szándékot, és a fordító biztosítja, hogy a szándék megvalósuljon.
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 dotnet new consolehasználatával. 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ázható annotációs környezetben legyen lefordítva. Nyissa meg a .csproj fájlt, és adjon hozzá egy Nullable elemet a PropertyGroup elemhez. Állítsa enableértékre. A C# 11/ .NET 7 előtt létrehozott projektekben a null értékű referenciatípusok funkciót kell választania. 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éssel, előfordulhat, hogy nem tükrözi pontosan az eredeti tervezési szándékot:
<Nullable>enable</Nullable>
Az alkalmazás típusainak megtervezése
Ehhez a felmérési alkalmazáshoz létre kell hoznia az alábbi osztályokat:
- Egy osztály, amely a kérdések listáját modellozza.
- Egy osztály, amely a felméréshez felvett személyek listáját modellozza.
- Egy osztály, amely egy felmérést készítő személy válaszait modellozza.
Ezek a típusok null értékű és nem null értékű hivatkozástípusokat is használnak annak kifejezésére, hogy mely tagokra van szükség, és mely tagok választhatók. A null értékű referenciatípusok egyértelműen kifejezik a tervezés szándékát.
- 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. Szeretné nyomon követni a megkeresett személyeket, még azokat a válaszadókat is, akik visszautasították a részvételt.
- Egy kérdésre adott válasz null értékű lehet. A válaszadók elutasíthatnak néhány vagy az összes kérdés megválaszolását.
Lehet, hogy annyira hozzászokott a nullát is megengedő hivatkozástípusok használatához, hogy esetleg figyelmen kívül hagy más lehetőségeket, amikor nem null értékű példányokat deklarálhatna.
- A kérdések gyűjteménye nem lehet nullértékű.
- A válaszadók gyűjteménye nem lehet null értékeket tartalmazó.
A kód megírása során láthatja, hogy egy nem nullát elfogadó hivatkozási típus alapértelmezettkénti beállítása a hivatkozásokhoz elkerüli azokat a gyakori hibákat, amelyek null értékű hivatkozásokhoz vezethetnek NullReferenceException. Az oktatóanyag egyik tanulsága, hogy döntéseket hozott 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 buildelt 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 ál-véletlenszerű halmazát hozza létre.
- Kapcsolatba lép a válaszadókkal, amíg a befejezett felmérések száma nem éri el a kitűzött célt.
- Fontos statisztikákat ír ki a felmérésre adott válaszokról.
A felmérés létrehozása nullábilis és nem nullábilis referenciatípusokkal
Az első megírt kód létrehozza a felmérést. Osztályokat ír 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ípussal rendelkezik, 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 public SurveyQuestion osztályt:
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
A fordító minden referenciatípusú változó deklarációját nem nullázható hivatkozási típusként értelmezi az engedélyezett nullálható annotációs környezetben. Az első figyelmeztetést úgy tekintheti meg, hogy tulajdonságokat ad hozzá a kérdés szövegéhez és a kérdés típusához, ahogyan az a következő kódban is látható:
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Mivel nem inicializáltad a QuestionText, a fordító figyelmeztetést ad arról, hogy egy nem-null értékű tulajdonság nincs inicializálva. A terv megkívánja, hogy a kérdésszöveg ne legyen null értékű, ezért hozzáad egy konstruktort, amely inicializálja a kérdésszöveget és a 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 argumentuma is egy nem-null referenciatípus, így a fordító nem ad ki figyelmeztetéseket.
Ezután hozzon létre egy publicnevű SurveyRun osztályt. Ez az osztály SurveyQuestion objektumok és metódusok listáját tartalmazza a felméréshez kérdések hozzáadásához, ahogyan az az alábbi kódban is látható:
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 ellenőrzések, mert a fordító segít kikényszeríteni a null értéktől mentes szerződést: A változót nem null értékűnek nyilvánította. Bár a fordító figyelmeztet a lehetséges null hozzárendelésekre, a futásidő során a null értékek továbbra is előfordulhatnak. Nyilvános API-k esetén érdemes lehet argumentumérvényesítést hozzáadni a nem null értékű referenciatípusokhoz is, mivel előfordulhat, hogy az ügyfélkód nem rendelkezik engedélyezve null értékű hivatkozástípusokkal, vagy szándékosan null értéket ad át.
Váltson a szerkesztőben a Program.cs fájlra, és cserélje le a Main tartalmát a következő kódsorokkal:
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 az egész projekt egy engedélyezett nullázható annotációs környezetben van, figyelmeztetések jelennek meg, amikor olyan metódusnak adja át null, amely nem nullázható hivatkozástípust vár. Próbálja ki az alábbi sor hozzáadásával a Main-hoz:
surveyRun.AddQuestion(QuestionType.Text, default);
Válaszadók létrehozása és válaszok leké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 kis feladatot is magában foglal:
- Hozzon létre egy metódust, amely válaszadó objektumokat hoz létre. Ezek az objektumok a felmérés kitöltésére felkért személyeket jelölik.
- Logikát hozhat létre a válaszadóknak feltett kérdések szimulálásához, és válaszokat gyűjthet, vagy észreveheti, hogy a válaszadó nem válaszolt.
- Ismételje meg, amíg elegendő válaszadó nem válaszol a felmérésre.
Szüksége van 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 Id tulajdonságot é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 static metódust, amellyel új résztvevőket hozhat létre 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 résztvevők válaszait generálja a felmérésben szereplő kérdésekre. 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á, adjon vissza egy hiányzó (vagy null) választ.
- Tegye fel az egyes kérdéseket, és rögzítse a választ. Előfordulhat, hogy minden válasz hiányzik (vagy null).
Adja hozzá a következő kódot a 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árterülete egy Dictionary<int, string>?, amely azt jelzi, hogy null értékű lehet. Az új nyelvi funkcióval deklarálhatja a tervezési szándékot, mind a fordítónak, mind a kód későbbi olvasóinak. Ha valaha dereferálja a surveyResponses, anélkül, hogy először ellenőrizné a null értékét, fordító figyelmeztetést ad. Nem kap figyelmeztetést a AnswerSurvey metódusban, mert a fordító meg tudja állapítani, hogy a surveyResponses változó nem null értékű volt az előző kódban.
A null hiányzó válaszokhoz való használata kiemeli a null értékű referenciatí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 a cél annak biztosítása, hogy az ön által írt kód kifejezze a terv szándékát. A hiányzó értékek a kódban való kifejezéshez szükséges fogalmak. A null érték egyértelmű módja a hiányzó értékek kifejezésének. Az összes null érték eltávolításának megkísérlése a hiányzó értékek nullkifejezésének más módon történő definiálásához vezet.
Ezután meg kell írnia a PerformSurvey metódust a SurveyRun osztályba. 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 válaszadóknak. Figyelje meg, hogy a válaszadókat addig adják hozzá, amíg elegendő hozzájárulást nem kapnak.
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ési válaszok vizsgálata
Az utolsó lépés a felmérés eredményeinek megjelenítése. Kódot ad hozzá számos ön által írt osztályhoz. Ez a kód a null értékű és a nem null értékű hivatkozástípusok megkülönböztetésének értékét mutatja be. Először adja hozzá a következő két kifejezéssel testesített tagot a SurveyResponse osztályhoz:
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Mivel surveyResponses null értékű hivatkozástípus, 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ázható karakterláncot ad vissza, ezért a hiányzó válasz esetét a null-egyesítő operátorral kell fedeznünk.
Ezután adja hozzá ezt a három kifejezéssel testesített tagot a 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 eltávolítja a ??-át és az azt követő üres karakterláncot, a fordító figyelmezteti önt, hogy a metódus visszaadhatja az null-et. A visszatérési aláírás egy 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");
}
}
Ebben a kódban nincs szükség null ellenőrzésre, mert úgy tervezte meg a mögöttes interfészeket, hogy azok mindegyike nem null értékű hivatkozástípusokat adjon vissza. A fordító statikus elemzése segít ezeknek a tervezési szerződéseknek a betartásában.
A kód lekérése
A kész oktatóanyag kódját a minták tárházában a csharp/NullableIntroduction mappában szerezheti be.
Kísérletezzen a típusdeklarációk null értékű és nem null értékű hivatkozástípusok közötti módosításával. Lássa, hogyan generál ez különböző figyelmeztetéseket annak érdekében, hogy véletlenül ne hivatkozzon ki egy null-t.
Következő lépések
Megtudhatja, hogyan használhat null értékű hivatkozástípust az Entity Framework használatakor: