Alıştırma - Null güvenlik stratejileri uygulama
Önceki ünitede null atanabilirlik amacınızı kodda ifade etme hakkında bilgi edinmişsinizdir. Bu ünitede öğrendiklerini mevcut bir C# projesine uygulayacaksınız.
Not
Bu modülde yerel geliştirme için .NET CLI (Komut Satırı Arabirimi) ve Visual Studio Code kullanılır. Bu modülü tamamladıktan sonra Visual Studio (Windows), Mac için Visual Studio (macOS) veya Visual Studio Code (Windows, Linux ve macOS) kullanarak geliştirmeye devam ederek kavramları uygulayabilirsiniz.
Bu modülde .NET 6.0 SDK kullanılır. Tercih ettiğiniz terminalde aşağıdaki komutu çalıştırarak .NET 6.0'ın yüklü olduğundan emin olun:
dotnet --list-sdks
Aşağıdakine benzer bir çıkış görüntülenir:
3.1.100 [C:\program files\dotnet\sdk]
5.0.100 [C:\program files\dotnet\sdk]
6.0.100 [C:\program files\dotnet\sdk]
ile 6 başlayan bir sürümün listelendiğinden emin olun. Hiçbiri listelenmiyorsa veya komut bulunamazsa en son .NET 6.0 SDK'sını yükleyin.
Örnek kodu alma ve inceleme
Komut terminalinde örnek GitHub deposunu kopyalayın ve kopyalanan dizine geçin.
git clone https://github.com/microsoftdocs/mslearn-csharp-null-safety cd mslearn-csharp-null-safetyVisual Studio Code'da proje dizinini açın.
code .komutunu kullanarak
dotnet runörnek projeyi çalıştırın.dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csprojBu, bir NullReferenceException atılmasıyla sonuçlanır.
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 13Yığın izlemesi, özel durumun .\src\ContosoPizza.Service\Program.cs içindeki 13. satırda oluştuğuna işaret eder. 13. satırda
Addyöntemi özelliğinde çağrılırpizza.Cheeses. olduğundanpizza.Cheesesnull, bir NullReferenceException oluşturulur.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! */
Boş değer atanabilir bağlamı etkinleştirme
Şimdi boş değer atanabilir bir bağlamı etkinleştirip derleme üzerindeki etkisini inceleyeceksiniz.
src/ContosoPizza.Service/ContosoPizza.Service.csproj içinde, vurgulanan satırı ekleyin ve değişikliklerinizi kaydedin:
<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>Yukarıdaki değişiklik, projenin tamamı
ContosoPizza.Serviceiçin null atanabilir bağlamı etkinleştirir.src/ContosoPizza.Models/ContosoPizza.Models.csproj içinde, vurgulanan satırı ekleyin ve değişikliklerinizi kaydedin:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>Yukarıdaki değişiklik, projenin tamamı
ContosoPizza.Modelsiçin null atanabilir bağlamı etkinleştirir.komutunu kullanarak
dotnet buildörnek çözümü oluşturun.dotnet buildDerleme 2 uyarıyla başarılı olur.
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.48komutunu kullanarak örnek çözümü
dotnet buildoluşturun.dotnet buildBu kez derleme hiçbir hata veya uyarı olmadan başarılı olur. Önceki derleme, uyarılarla başarıyla tamamlandı. Kaynak değişmediğinden derleme işlemi derleyiciyi yeniden çalıştırmaz. Derleme derleyiciyi çalıştırmadığından hiçbir uyarı yoktur.
İpucu
komutundan önce komutunu kullanarak projedeki tüm derlemelerin yeniden derlenmesine
dotnet cleandotnet buildzorlayabilirsiniz..csproj dosyalarına vurgulanan satırları ekleyin ve değişikliklerinizi kaydedin.
<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>Önceki değişiklikler, derleyiciye bir uyarıyla karşılaşıldığında derlemenin başarısız olmasını bildirir.
İpucu
kullanımı
<TreatWarningsAsErrors>isteğe bağlıdır. Ancak hiçbir uyarıyı gözden kaçırmamanızı sağladığı için bunu öneririz.komutunu kullanarak
dotnet buildörnek çözümü oluşturun.dotnet buildDerleme 2 hatayla başarısız oluyor.
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.95Uyarıları hata olarak ele alırken uygulama artık derlenmez. Bu durum aslında istenen bir durumdur çünkü hata sayısı azdır ve bunları hızla ele alacağız. İki hata (CS8618), henüz başlatılmamış null atanamaz olarak bildirilen özellikler olduğunu size bildirir.
Hataları düzeltme
Null atanabilirlikle ilgili uyarıları/hataları çözmek için birçok taktik vardır. Bazı Örnekler:
- Oluşturucu parametreleri olarak boş değer atanamayan peynir ve topping koleksiyonu gerektirme
- Özelliğin
get/setyolunu kesme ve denetimnullekleme - Özelliklerin null atanabilir olma amacını ifade etme
- Özellik başlatıcılarını kullanarak koleksiyonu varsayılan (boş) satır içi değerle başlatın
- Özelliğine oluşturucuda varsayılan (boş) bir değer atayın
Özelliğindeki hatayı
Pizza.Cheesesdüzeltmek için Pizza.cs'da özellik tanımını değiştirerek birnulldenetim ekleyin. Peynirsiz pizza değil, değil mi?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(); }Önceki kodda:
- adlı
getvesetözellik erişimcilerini kesmeye_cheesesyardımcı olmak için yeni bir yedekleme alanı eklenir. Null atanabilir (?) olarak bildirilip başlatılmamış olarak bırakılır. - Erişimci
get, null birleşim işlecini (??kullanan bir ifadeyle eşlenir. Bu ifade, olmadığını_cheesesvarsayarak alanını döndürürnull. isenull, döndürmeden_cheesesönce öğesininew List<PizzaCheese>()atar_cheeses. - Erişimci
setde bir ifadeye eşlenir ve null birleşim işlecini kullanır. Bir tüketici birnulldeğer ArgumentNullException atadığında oluşturulur.
- adlı
Tüm pizzalarda topping'ler olmadığından,
nullözellik içinPizza.Toppingsgeçerli bir değer olabilir. Bu durumda, null atanabilir olarak ifade etmek mantıklıdır.Pizza.csözellik tanımını null atanabilir olacak şekilde değiştirin.
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(); }ToppingsÖzelliği artık null atanabilir olarak ifade edilir.Vurgulanan satırı ContosoPizza.Service\Program.cs ekleyin:
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! */
Yukarıdaki kodda null birleşim işleci, ise
Toppingsöğesine atamaknew List<PizzaTopping>();içinnullkullanılır.
Tamamlanmış çözümü çalıştırma
Tüm değişikliklerinizi kaydedin ve çözümü oluşturun.
dotnet buildDerleme hiçbir uyarı veya hata olmadan tamamlar.
Uygulamayı çalıştırma.
dotnet run --project src/ContosoPizza.Service/ContosoPizza.Service.csprojUygulama tamamlanmaya (hata olmadan) çalışır ve aşağıdaki çıkışı görüntüler:
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!
Özet
Bu ünitede, kodunuzda olası NullReferenceException oluşumları tanımlamak ve önlemek için null atanabilir bir bağlam kullandınız.