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.
Jótanács
Még nem ismeri a null értékű referenciatípusokat? Először olvassa el a null értékű hivatkozástípusokat . Ez az oktatóanyag feltételezi, hogy tisztában van a nem null értékű és a null értékű hivatkozástípusok közötti különbségekkel, valamint azzal, hogy a fordító hogyan követi nyomon a null állapotot.
Más nyelvről jön? Ha a Kotlin null értékű típusait, TypeScript vagy strictNullChecksSwift választható elemeit használta, a koncepciómodell közvetlenül leképezhető. A gyakorlat itt a tervezési szándék kifejezéséről szól, nem a szintaxis elsajátításáról.
Ebben az oktatóanyagban egy kis könyvtárat hoz létre, amely egy felmérés lebonyolítását modellezi. Az adatok két különböző mintával rendelkeznek, amelyek null értékű hivatkozástípusokkal különböztethetők meg:
- Egy felmérési kérdésnek mindig jelen kell lennie. A kérdések listája és az egyes kérdések szövege soha nem lehet
null. - Előfordulhat , hogy hiányzik egy kérdésre adott válasz . A válaszadók elutasíthatnak néhány vagy az összes kérdés megválaszolását, és a modellnek explicitnek kell lennie.
Ezeket a szabályokat nem nullázható és nullázható hivatkozástípusokkal deklarálja. A fordító ezután figyelmeztet, ha a kód viselkedése nem egyezik a tervével.
Ebben az útmutatóban Ön:
- Hozza létre az alkalmazást.
- Készítse el a felmérésre vonatkozó kérdéseket.
- Állítson össze egy kérdőívet.
- Tesztelje a nem null érték követelményét.
- Választípusok létrehozása.
- Válaszadók létrehozása.
- Hozzon létre egy felmérési választ.
- Felmérési válaszok készletének létrehozása.
- Vizsgálja meg a felmérés eredményeit.
A felmérést három osztály modellezheti:
-
SurveyQuestion: egy kérdés. A szöveg és a kérdés típusa kötelező. -
SurveyRun: a kérdések gyűjteménye, valamint a válaszadók listája. -
SurveyResponse: egy válaszadó válasza, amely esetleg hiányzik.
Minden típus nem null értékű referenciatípusokat használ a szükséges értékekhez, a hiányzó értékekhez pedig null értékű referenciatípusokat.
Prerequisites
- A legújabb .NET SDK
- Visual Studio Code szerkesztő
- A C# fejlesztőkészlet
Ez az oktatóanyag feltételezi, hogy ismeri a C#-t, és vagy Visual Studio vagy a .NET parancssori felületet.
Az alkalmazás létrehozása és null értékű hivatkozástípusok engedélyezése
Hozzon létre egy új konzolalkalmazást a következő néven NullableIntroduction:
dotnet new console -n NullableIntroduction
cd NullableIntroduction
Felmérési kérdések összeállítása
Adjon hozzá egy új, a projekthez elnevezett SurveyQuestion.cs fájlt, és cserélje le annak tartalmát a következő kódra. A szöveg és a kérdés típusa nem null értékű, ezért a konstruktornak mindkettőt inicializálnia kell:
namespace NullableIntroduction;
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion(QuestionType typeOfQuestion, string text)
{
public string QuestionText { get; } = text;
public QuestionType TypeOfQuestion { get; } = typeOfQuestion;
}
A konstruktor paraméterei nem null értékű referenciatípusok, ezért a fordító figyelmezteti a hívót, ha bármelyik argumentum lehet null.
Kérdések felmérésének összeállítása
Ezután adjon hozzá egy új fájlt a projekthez, SurveyRun.cs és definiáljon egy osztályt SurveyRun a kérdések listájának tárolásához:
namespace NullableIntroduction;
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = [];
public void AddQuestion(QuestionType type, string question) =>
AddQuestion(new SurveyQuestion(type, question));
public void AddQuestion(SurveyQuestion surveyQuestion) =>
surveyQuestions.Add(surveyQuestion);
}
A surveyQuestions mező egy olyan List<SurveyQuestion>, amely nem lehet null értékű.
Gyűjteménykifejezéssel inicializál egy üres listát. Mindkét AddQuestion túlterhelés nem nullázható paramétereket fogad el, ezért a fordító kikényszeríti, hogy a hívók ne adhassanak át null értéket.
A(z) Program.cs elemben hozzon létre egy SurveyRun elemet, és adjon hozzá három kérdést:
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?");
A not-null követelmény tesztelése
Ha meg szeretné tudni, hogy a fordító hogyan kényszeríti ki a nem null értékű paramétereket, próbálkozzon a következő sor hozzáadásával és újraépítésével:
surveyRun.AddQuestion(QuestionType.Text, default);
A fordító a CS8625 figyelmeztetést adja ki, mert a(z) default referenciatípus esetén null értékre kiértékelődik, a(z) AddQuestion pedig nem nullázható string értéket vár. A folytatás előtt távolítsa el a vonalat.
Választípusok létrehozása
A válaszadók elutasíthatják a felmérést, és még akkor is, ha részt vesznek, kihagyhatják az egyes kérdéseket. A "hiányzó" mindkét formája érvényes eredmény, és a típusrendszernek láthatóvá kell tennie őket. Mindkét formát a null használatával fejezi ki.
Adjon hozzá egy új, a projekthez elnevezett SurveyResponse.cs fájlt, és definiáljon egy osztályt SurveyResponse . Használjon elsődleges konstruktort (a típuson deklarált paramétereket, amelyek a teljes törzsben elérhetők) a mindig szükséges Idkonstruktor rögzítéséhez:
namespace NullableIntroduction;
public class SurveyResponse(int id)
{
public int Id { get; } = id;
}
Válaszadók létrehozása
Adjon hozzá egy statikus gyári metódust (egy metódust static , amely létrehoz és visszaad egy új típusú példányt, amely a konstruktor közvetlen meghívásának alternatíva), amely véletlenszerű azonosítóval hozza létre a válaszadókat:
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
Egyetlen felmérési válasz létrehozása
Ezután adja hozzá azt a metódust, amely felteszi a kérdőívet a válaszadónak. A válaszokat egy null értékű szótárban tárolja, így maga a típus közli, hogy a válaszadó elutasíthatja:
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 surveyResponses mező Dictionary<int, string>?. Ha a mezőre anélkül hivatkozik, hogy előbb ellenőrizné, hogy null, a fordító figyelmeztetést ad. A AnswerSurvey belsejében a fordító nyomon követi, hogy a surveyResponsesnem null közvetlenül a new kifejezés után, ezért a ciklusmag nem igényel további ellenőrzést.
Felmérési válaszok készletének létrehozása
Adjon hozzá egy metódust SurveyRun , amely összeállítja a válaszadók listáját, amíg elegendő hozzájárulást nem kap a részvételhez:
private List<SurveyResponse>? respondents;
public void PerformSurvey(int numberOfRespondents)
{
int respondentsConsenting = 0;
respondents = [];
while (respondentsConsenting < numberOfRespondents)
{
var respondent = SurveyResponse.GetRandomId();
if (respondent.AnswerSurvey(surveyQuestions))
respondentsConsenting++;
respondents.Add(respondent);
}
}
A respondents mező List<SurveyResponse>? – null, amíg a felmérés le nem fut.
Hívás PerformSurvey innen Main:
surveyRun.PerformSurvey(50);
A felmérés eredményeinek vizsgálata
Az eredmények jelentéséhez tegyen elérhetővé néhány segédfüggvényt a SurveyResponse és a SurveyRun elemből. A(z) SurveyResponse elemhez adjon hozzá kifejezéstörzsű tagokat (olyan tagokat, amelyek egy { ... } blokk helyett => és egyetlen kifejezés használatával vannak definiálva), amelyek a null értékű szótárt kezelik:
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
AnsweredSurvey ellenőrzi a mezőt a(z) null alapján.
Answer a ?. nullfeltételes operátort használja (amely null értékre értékelődik ki, ha a bal oldal null, kivételdobás helyett) a biztonságos dereferáláshoz, valamint a ?? null-egyesítő operátort (amely a jobb oldali operandust használja, ha a bal oldali null) egy nem null értékű tartalékérték biztosítására. A metódus visszatérési típusa nem null értékű string, ezért a hívóknak nincs szükségük null értékű ellenőrzésekre.
A(z) SurveyRun elemhez adjon hozzá olyan kifejezéstörzsű tagokat, amelyek elérhetővé teszik a résztvevők és a kérdések listáját:
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
AllParticipants nem nullázható sorozatot ad vissza, annak ellenére, hogy a respondents lehet null. Az ?? operátor a(z) Enumerable.Empty<SurveyResponse>() értéket helyettesíti be, ha a mező még nincs kitöltve. Ha eltávolítja a ?? záradékot, a fordító arra figyelmeztet, hogy a metódus a nem nullázható visszatérési típusa ellenére is null értéket adhat vissza.
Végül írja meg a jelentést a Main 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");
}
}
Figyelje meg, hogy nincs szükség null értékű ellenőrzésre az participant, surveyRun.Questionsvagy surveyRun.GetQuestion(i). A típusok nem null értékűként deklarálják ezeket az értékeket, így a fordító a ciklus során nem null értékűként kezeli őket.
Futtassa az alkalmazást:
dotnet run
A kimenet minden futtatáskor eltérő, mert a válaszadókat véletlenszerűen generálják, de minden sor vagy egy résztvevő válaszait tartalmazza, vagy azt jelzi, hogy nem kívánt válaszolni.
Conclusion
A kész minta a dotnet/samples adattár csharp/NullableIntroduction mappájában található. Kísérletezzen a null értékű és a nem null értékű típusok módosításával. Ha eltávolít egy ? elemet egy olyan kialakításban, amely lehetővé teszi a hiányzó értékeket, a fordítóprogram figyelmeztetéseket ad, amelyek minden olyan helyre rámutatnak, ahol a hiányzó érték számít.