Gyakorlat – Null biztonsági stratégiák alkalmazása
Az előző leckében megismerkedett a null érték kezelési 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 szerepel egy verzió, amely 6-vel kezdődik 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-safetyNyissa meg a projektkönyvtárat a Visual Studio Code-ban.
code .Futtassa a mintaprojektet a
dotnet runparanccsal.dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csprojEz 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 13A veremnyomkövetési napló azt jelzi, hogy a kivétel a 13. sorban történt az .\src\ContosoPizza.Service\Program.cs-ben. A 13. sorban a
Addmetódus meghívása apizza.Cheesestulajdonságon történik. Mivelpizza.Cheesesvannull, egy NullReferenceException eldobásra kerül.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éket megengedő környezet engedélyezése
Most pedig engedélyezi a nullálható kontextust, és megvizsgálja 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.Serviceprojekt számára a nullálható környezetet.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.Modelsprojekt számára a nullálható környezetet.Hozza létre a mintamegoldást a
dotnet buildparancs használatával.dotnet buildA 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.48Hozza létre újra a mintamegoldást a
dotnet buildparanccsal.dotnet buildA 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 cleanelőződotnet buildparancs 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 buildparancs használatával.dotnet buildA 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.95Figyelmeztetések hibaként való kezelésekor az alkalmazás nem készül el. 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 nem null értékűként vannak deklarálva, de még nem lettek inicializá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:
- Követeljen egy nem null értékű sajt- és öntetgyűjteményt konstruktorparaméterként
- A
get/settulajdonság elfogása és egynullellenőrzés 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 soron belül, tulajdonság inicializálók segítségével.
- A tulajdonság hozzárendelése alapértelmezett (üres) értékhez a konstruktorban
A
Pizza.Cheesestulajdonság hibájának kijavításához módosítsa a Pizza.cs fájlban a tulajdonság definícióját, hogy hozzáadjon egynullellenőrzést. 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:
- Egy új háttérmezőt adnak hozzá, amely segít elfogni a
getéssethozzáférési metódusokat a_cheesesnevű tulajdonsághoz. Null értékűnek (?) van deklarálva, és nem inicializálva marad. - A
gethozzáférő egy null-egyesítő operátort (??) használó kifejezésre van térképezve. Ez a kifejezés a_cheesesmezőt adja vissza, feltéve, hogy nemnull. Hanull, a visszatérés előtt_cheeses-t rendel hozzá anew List<PizzaCheese>()-hez, majd visszatér a_cheeses-hez. - A
setlekérdező egy kifejezésre is le van képezve, és a null-egyesítő operátort használja. Amikor egy fogyasztó hozzárendel egynullértéket, a ArgumentNullException kidobás történik.
- Egy új háttérmezőt adnak hozzá, amely segít elfogni a
Mivel nem minden pizzán van öntet, a
nullérvényes érték lehet aPizza.Toppingstulajdonság számára. Ebben az esetben érdemes null értékűként kifejezni.Módosítsa a tulajdonságdefiníciót a Pizza.cs , hogy
Toppingsnull é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
Toppingstulajdonság mostantól null értékűként van kifejezve.Adja hozzá a kiemelt sort a következőhöz: 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 egyesítő operátort használjuk, hogy
Toppings-t hozzárendeljünknew List<PizzaTopping>();-hez, ha aznull.
A kész megoldás futtatása
Mentse az összes módosítást, majd hozza létre a megoldást.
dotnet buildA build figyelmeztetések és hibák nélkül befejeződik.
Futtassa az alkalmazást.
dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csprojAz 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 az egységben nullálható környezetet használt a kód lehetséges NullReferenceException előfordulásainak azonosítására és megelőzésére.