Gyakorlat – Null biztonsági stratégiák alkalmazása
Az előző leckében megismerkedett a nullability szándék kódban való kifejezésével. Ebben a leckében a tanultakat egy meglévő C#-projektre fogja alkalmazni.
Feljegyzés
Ez a modul a .NET CLI-t (parancssori felületet) és a Visual Studio Code-ot használja a helyi fejlesztéshez. A modul elvégzése után alkalmazhatja a fogalmakat a Visual Studio (Windows), a Mac Visual Studio (macOS) vagy a Visual Studio Code (Windows, Linux és macOS) használatával történő folyamatos fejlesztéssel.
Ez a modul a .NET 6.0 SDK-t használja. Győződjön meg arról, hogy telepítve van a .NET 6.0, ha az alábbi parancsot futtatja az előnyben részesített terminálban:
dotnet --list-sdks
A következőhöz hasonló kimenet jelenik meg:
3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]
Győződjön meg arról, hogy az első 6
verzió szerepel a listában. Ha egyik sem szerepel a listában, vagy a parancs nem található, telepítse a legújabb .NET 6.0 SDK-t.
A mintakód lekérése és vizsgálata
Egy parancsterminálban klónozza a gitHub-mintaadattárat, és váltson a klónozott könyvtárra.
git clone https://github.com/microsoftdocs/mslearn-csharp-null-safety cd mslearn-csharp-null-safety
Nyissa meg a projektkönyvtárat a Visual Studio Code-ban.
code .
Futtassa a mintaprojektet a
dotnet run
paranccsal.dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csproj
Ez egy dobáshoz vezet NullReferenceException .
dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csproj Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object. at Program.<Main>$(String[] args) in .\src\ContosoPizza.Service\Program.cs:line 13
A verem nyomkövetése azt jelzi, hogy a kivétel a 13. sorban történt az .\src\ContosoPizza.Service\Program.cs. A 13. sorban a
Add
metódus meghívása apizza.Cheeses
tulajdonságon történik. Mivelpizza.Cheeses
vannull
, a NullReferenceException dobás.using ContosoPizza.Models; // Create a pizza Pizza pizza = new("Meat Lover's Special") { Size = PizzaSize.Medium, Crust = PizzaCrust.DeepDish, Sauce = PizzaSauce.Marinara, Price = 17.99m, }; // Add cheeses pizza.Cheeses.Add(PizzaCheese.Mozzarella); pizza.Cheeses.Add(PizzaCheese.Parmesan); // Add toppings pizza.Toppings.Add(PizzaTopping.Sausage); pizza.Toppings.Add(PizzaTopping.Pepperoni); pizza.Toppings.Add(PizzaTopping.Bacon); pizza.Toppings.Add(PizzaTopping.Ham); pizza.Toppings.Add(PizzaTopping.Meatballs); Console.WriteLine(pizza); /* Expected output: The "Meat Lover's Special" is a deep dish pizza with marinara sauce. It's covered with a blend of mozzarella and parmesan cheese. It's layered with sausage, pepperoni, bacon, ham and meatballs. This medium size is $17.99. Delivery is $2.50 more, bringing your total $20.49! */
Null értékű környezet engedélyezése
Most engedélyezi a null értékű környezeteket, és megvizsgálja annak a buildre gyakorolt hatását.
Az src/ContosoPizza.Service/ContosoPizza.Service.csproj fájlban adja hozzá a kiemelt sort, és mentse a módosításokat:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\ContosoPizza.Models\ContosoPizza.Models.csproj" /> </ItemGroup> </Project>
Az előző módosítás lehetővé teszi a teljes
ContosoPizza.Service
projekt null értékű környezetét.Az src/ContosoPizza.Models/ContosoPizza.Models.csproj fájlban adja hozzá a kiemelt sort, és mentse a módosításokat:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
Az előző módosítás lehetővé teszi a teljes
ContosoPizza.Models
projekt null értékű környezetét.Hozza létre a mintamegoldást a
dotnet build
parancs használatával.dotnet build
A build 2 figyelmeztetéssel sikeres.
dotnet build Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... Restored .\src\ContosoPizza.Service\ContosoPizza.Service.csproj (in 477 ms). Restored .\src\ContosoPizza.Models\ContosoPizza.Models.csproj (in 475 ms). .\src\ContosoPizza.Models\Pizza.cs(3,28): warning CS8618: Non-nullable property 'Cheeses' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] .\src\ContosoPizza.Models\Pizza.cs(3,28): warning CS8618: Non-nullable property 'Toppings' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] ContosoPizza.Models -> .\src\ContosoPizza.Models\bin\Debug\net6.0\ContosoPizza.Models.dll ContosoPizza.Service -> .\src\ContosoPizza.Service\bin\Debug\net6.0\ContosoPizza.Service.dll Build succeeded. .\src\ContosoPizza.Models\Pizza.cs(3,28): warning CS8618: Non-nullable property 'Cheeses' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] .\src\ContosoPizza.Models\Pizza.cs(3,28): warning CS8618: Non-nullable property 'Toppings' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] 2 Warning(s) 0 Error(s) Time Elapsed 00:00:07.48
Hozza létre újra a mintamegoldást a
dotnet build
paranccsal.dotnet build
A build ezúttal hibák és figyelmeztetések nélkül sikeres lesz. Az előző build sikeresen befejeződött, figyelmeztetésekkel. Mivel a forrás nem változott, a buildelési folyamat nem futtatja újra a fordítót. Mivel a build nem futtatja a fordítót, nincsenek figyelmeztetések.
Tipp.
A projekt összes szerelvényének újraépítését az
dotnet clean
előződotnet build
parancs használatával kényszerítheti.A .csproj fájlokban adja hozzá a kiemelt sorokat, és mentse a módosításokat.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\ContosoPizza.Models\ContosoPizza.Models.csproj" /> </ItemGroup> </Project>
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <TreatWarningsAsErrors>true</TreatWarningsAsErrors> </PropertyGroup> </Project>
Az előző módosítások arra utasítják a fordítót, hogy minden figyelmeztetés esetén meghiúsuljon a buildelés.
Tipp.
A használat
<TreatWarningsAsErrors>
nem kötelező. Javasoljuk azonban, hogy ne hagyja figyelmen kívül a figyelmeztetéseket.Hozza létre a mintamegoldást a
dotnet build
parancs használatával.dotnet build
A build 2 hibával meghiúsul.
dotnet build Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. .\src\ContosoPizza.Models\Pizza.cs(3,28): error CS8618: Non-nullable property 'Cheeses' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] .\src\ContosoPizza.Models\Pizza.cs(3,28): error CS8618: Non-nullable property 'Toppings' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] Build FAILED. .\src\ContosoPizza.Models\Pizza.cs(3,28): error CS8618: Non-nullable property 'Cheeses' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] .\src\ContosoPizza.Models\Pizza.cs(3,28): error CS8618: Non-nullable property 'Toppings' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. [.\src\ContosoPizza.Models\ContosoPizza.Models.csproj] 0 Warning(s) 2 Error(s) Time Elapsed 00:00:02.95
A figyelmeztetések hibákként való kezelésekor az alkalmazás már nem épül fel. Ebben a helyzetben ez valóban kívánatos, mivel a hibák száma kicsi, és gyorsan megoldjuk őket. A két hiba (CS8618) tudatja Önnel, hogy vannak olyan tulajdonságok, amelyek még nem inicializálva vannak, nem null értékűként vannak deklarálva.
A hibák elhárítása
A nulllással kapcsolatos figyelmeztetések/hibák elhárítására számos taktikát lehet találni. Néhány példa:
- Nem null értékű sajt- és öntetgyűjtemény megkövetelése konstruktorparaméterként
- A tulajdonság
get
/set
elfogása és ellenőrzésnull
hozzáadása - A tulajdonságok null értékűvé nyilvánítási szándékának kifejezése
- A gyűjtemény inicializálása alapértelmezett (üres) értékkel a beágyazott tulajdonság inicializálóival
- A tulajdonság hozzárendelése alapértelmezett (üres) értékhez a konstruktorban
A tulajdonság hibáinak
Pizza.Cheeses
kijavításához módosítsa a tulajdonságdefiníciót a Pizza.cs egy ellenőrzés hozzáadásáhoznull
. Ez nem igazán pizza sajt nélkül, ugye?namespace ContosoPizza.Models; public sealed record class Pizza([Required] string Name) { private ICollection<PizzaCheese>? _cheeses; public int Id { get; set; } [Range(0, 9999.99)] public decimal Price { get; set; } public PizzaSize Size { get; set; } public PizzaCrust Crust { get; set; } public PizzaSauce Sauce { get; set; } public ICollection<PizzaCheese> Cheeses { get => (_cheeses ??= new List<PizzaCheese>()); set => _cheeses = value ?? throw new ArgumentNullException(nameof(value)); } public ICollection<PizzaTopping>? Toppings { get; set; } public override string ToString() => this.ToDescriptiveString(); }
A fenti kód a következőket végzi el:
- A rendszer új háttérmezőt ad hozzá, amely segít elfogni a névvel ellátott
_cheeses
ésset
aget
tulajdonsághoz tartozó tartozékokat. Null értékűnek (?
) van deklarálva, és nem inicializálva marad. - A
get
tartozék egy null-szenesítő operátort (??
) használó kifejezésre van leképezve. Ez a kifejezés a_cheeses
mezőt adja vissza, feltéve, hogy nemnull
. Ha így vannull
, a visszatérés előtt hozzárendelinew List<PizzaCheese>()
_cheeses
_cheeses
. - A
set
tartozék egy kifejezésre is le van képezve, és a null-szenesítő operátort használja. Amikor egy fogyasztó hozzárendel egynull
értéket, a ArgumentNullException kidobás történik.
- A rendszer új háttérmezőt ad hozzá, amely segít elfogni a névvel ellátott
Mivel nem minden pizza van öntet, lehet,
null
hogy érvényes érték aPizza.Toppings
tulajdonság. Ebben az esetben érdemes null értékűként kifejezni.Módosítsa a tulajdonságdefiníciót a Pizza.cs , hogy
Toppings
null értékű legyen.namespace ContosoPizza.Models; public sealed record class Pizza([Required] string Name) { private ICollection<PizzaCheese>? _cheeses; public int Id { get; set; } [Range(0, 9999.99)] public decimal Price { get; set; } public PizzaSize Size { get; set; } public PizzaCrust Crust { get; set; } public PizzaSauce Sauce { get; set; } public ICollection<PizzaCheese> Cheeses { get => (_cheeses ??= new List<PizzaCheese>()); set => _cheeses = value ?? throw new ArgumentNullException(nameof(value)); } public ICollection<PizzaTopping>? Toppings { get; set; } public override string ToString() => this.ToDescriptiveString(); }
A
Toppings
tulajdonság mostantól null értékűként van kifejezve.Adja hozzá a kiemelt sort a ContosoPizza.Service\Program.cs:
using ContosoPizza.Models; // Create a pizza Pizza pizza = new("Meat Lover's Special") { Size = PizzaSize.Medium, Crust = PizzaCrust.DeepDish, Sauce = PizzaSauce.Marinara, Price = 17.99m, }; // Add cheeses pizza.Cheeses.Add(PizzaCheese.Mozzarella); pizza.Cheeses.Add(PizzaCheese.Parmesan); // Add toppings pizza.Toppings ??= new List<PizzaTopping>(); pizza.Toppings.Add(PizzaTopping.Sausage); pizza.Toppings.Add(PizzaTopping.Pepperoni); pizza.Toppings.Add(PizzaTopping.Bacon); pizza.Toppings.Add(PizzaTopping.Ham); pizza.Toppings.Add(PizzaTopping.Meatballs); Console.WriteLine(pizza); /* Expected output: The "Meat Lover's Special" is a deep dish pizza with marinara sauce. It's covered with a blend of mozzarella and parmesan cheese. It's layered with sausage, pepperoni, bacon, ham and meatballs. This medium size is $17.99. Delivery is $2.50 more, bringing your total $20.49! */
Az előző kódban a null-szenesítő operátort használjuk annak hozzárendeléséhez
Toppings
new List<PizzaTopping>();
, ha aznull
.
A kész megoldás futtatása
Mentse az összes módosítást, majd hozza létre a megoldást.
dotnet build
A build figyelmeztetések és hibák nélkül befejeződik.
Futtassa az alkalmazást.
dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csproj
Az alkalmazás a befejezésig (hiba nélkül) fut, és a következő kimenetet jeleníti meg:
dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csproj The "Meat Lover's Special" is a deep dish pizza with marinara sauce. It's covered with a blend of mozzarella and parmesan cheese. It's layered with sausage, pepperoni, bacon, ham and meatballs. This medium size is $17.99. Delivery is $2.50 more, bringing your total $20.49!
Összegzés
Ebben a leckében null értékű környezetet használt a kód lehetséges NullReferenceException
előfordulásának azonosítására és megelőzésére.