Školení
Modul
Zabezpečení null v jazyce C# - Training
Naučte se kódovat postupy, které pomáhají zabránit výskytu NullReferenceException.
Tento prohlížeč se už nepodporuje.
Upgradujte na Microsoft Edge, abyste mohli využívat nejnovější funkce, aktualizace zabezpečení a technickou podporu.
Odkazové typy s možnou hodnotou Null jsou skupina vlastností, které minimalizují pravděpodobnost, že váš kód způsobí, že běhové prostředí vyvolá System.NullReferenceException. Tři funkce, které vám pomohou vyhnout se těmto výjimkám, včetně možnosti explicitně označit typ odkazu jako nullable:
null
proměnná před jeho odvozováním.Kompilátor sleduje stav null každého výrazu v kódu v době kompilace. Stav null má jednu ze dvou hodnot:
null
.null
.Poznámky k proměnným určují nulovou hodnotu proměnné typu odkazu:
null
hodnotu nebo výraz možná null , kompilátor vydá upozornění. Proměnné, které nemají hodnotu null , mají výchozí stav null not-null.null
hodnotu nebo výraz možná null . Pokud má proměnná hodnotu null-state je možná-null, kompilátor vydá upozornění, pokud proměnnou přeložíte. Výchozí stav null pro proměnnou je možná null.Zbytek tohoto článku popisuje, jak tyto tři oblasti funkcí fungují a vytvářejí upozornění, když váš kód může hodnotu null
. Dereferencing proměnné znamená přístup k jednomu ze svých členů pomocí operátoru .
(tečka), jak je znázorněno v následujícím příkladu:
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
Při dereference proměnné, jejíž hodnota je null
, modul runtime vyvolá System.NullReferenceExceptionvýjimku .
Podobně lze upozornění vytvořit, když []
se zápis používá pro přístup k členu objektu, pokud je null
objekt:
using System;
public class Collection<T>
{
private T[] array = new T[100];
public T this[int index]
{
get => array[index];
set => array[index] = value;
}
}
public static void Main()
{
Collection<int> c = default;
c[10] = 1; // CS8602: Possible derefence of null
}
Dozvíte se o:
?
je implementována odlišně pro typy hodnot s možnou hodnotou null a odkazové typy s možnou hodnotou null.Nakonec zjistíte známé nástrahy analýzy stavu null v struct
typech a polích.
Tyto koncepty můžete prozkoumat také v našem modulu Learn o bezpečnosti s možnou hodnotou Null v jazyce C#.
null
a od té doby nebyla přiřazena.Každá proměnná, kterou kompilátor nemůže určit jako neprazdná, se považuje za možná prázdná. Analýza poskytuje upozornění v situacích, kdy byste mohli omylem odvodit null
hodnotu. Kompilátor generuje upozornění na základě stavu null.
null
před jeho odvozováním.Představte si následující příklad:
string? message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
V předchozím příkladu kompilátor určuje, že message
při tisku první zprávy je možná null . U druhé zprávy není žádné upozornění. Poslední řádek kódu vytvoří upozornění, protože originalMessage
může mít hodnotu null. Následující příklad ukazuje praktičtější použití při procházení stromu uzlů do kořenového adresáře a zpracování jednotlivých uzlů během procházení:
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
Předchozí kód nevygeneruje žádná upozornění pro dereferencování proměnné current
. Statická analýza určuje, že current
se nikdy nezhoršuje, když je to možná null. Proměnná current
se kontroluje před null
current.Parent
přístupem a před předáním current
ProcessNode
akce. Předchozí příklady ukazují, jak kompilátor určuje stav null pro místní proměnné při inicializaci, přiřazení nebo porovnání s null
.
Analýza stavu null neprovádí trasování do volaných metod. V důsledku toho můžou pole inicializovaná v běžné pomocné metodě volané všemi konstruktory generovat upozornění s následující zprávou:
Při ukončení konstruktoru musí vlastnost name obsahovat nenulovou hodnotu.
Tato upozornění můžete řešit jedním ze dvou způsobů: řetězení konstruktoru nebo atributy s možnou hodnotou null v pomocné metodě. Následující kód ukazuje příklad každého z nich. Třída Person
používá společný konstruktor volaný všemi ostatními konstruktory. Třída Student
má pomocnou metodu anotovanou atributem System.Diagnostics.CodeAnalysis.MemberNotNullAttribute :
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
Analýza stavu s možnou hodnotou null a upozornění, která kompilátor generuje, vám pomůže vyhnout se chybám programu dereferencováním null
. Článek o řešení upozorněnísch Diagnostika vytvořená z analýzy stavu null jsou pouze upozornění.
Analýza stavu null potřebuje od vývojářů rady, aby porozuměli sémantice rozhraní API. Některá rozhraní API poskytují kontroly hodnot null a měly by změnit stav null proměnné z hodnoty null na hodnotu not-null. Jiná rozhraní API vrací výrazy, které nejsou null nebo možná null v závislosti na stavu null vstupních argumentů. Představte si například následující kód, který zobrazí zprávu velkými písmeny:
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
Na základě kontroly by každý vývojář považoval tento kód za bezpečný a neměl by generovat upozornění. Kompilátor však neví, že IsNull
poskytuje kontrolu hodnoty null a vydává upozornění pro message.ToUpper()
příkaz, přičemž zvažuje message
, že se má jednat o proměnnou s možnou hodnotou null . Toto upozornění opravíte pomocí atributu NotNullWhen
:
bool IsNull([NotNullWhen(false)] string? s) => s == null;
Tento atribut informuje kompilátor, že pokud IsNull
vrátí false
, parametr s
není null. Kompilátor změní stavmessage
null na hodnotu not-null uvnitř if (!IsNull(message)) {...}
bloku. Nejsou vydána žádná upozornění.
Atributy poskytují podrobné informace o stavu null argumentů, návratové hodnoty a členy instance objektu použité k vyvolání členu. Podrobnosti o jednotlivých atributech najdete v článku s referenčními informacemi jazyka o atributech odkazu s možnou hodnotou null. Od verze .NET 5 jsou všechna rozhraní API modulu runtime .NET opatřena poznámkami. Statickou analýzu vylepšíte přidáním poznámek k rozhraním API za účelem poskytnutí sémantických informací o stavu null argumentů a návratových hodnotách.
Analýza stavu null poskytuje robustní analýzu místních proměnných. Kompilátor od vás potřebuje další informace pro členské proměnné. Kompilátor potřebuje další informace k nastavení stavu null všech polí v počáteční závorce člena. K inicializaci objektu lze použít kterýkoli z přístupných konstruktorů. Pokud by mohlo být pole člena nastaveno na null
, kompilátor musí předpokládat, že jeho stav null je možná null na začátku každé metody.
Používáte poznámky, které mohou deklarovat, zda je proměnná typu odkazu s možnou hodnotou null nebo nenulový odkaz. Tyto poznámky dělají důležité prohlášení o stavu null pro proměnné:
null
. Kompilátor vydá upozornění, když kód přiřadí výraz možná null proměnné, která by neměla mít hodnotu null.null
odkaz: null
.null
a je možné jí přiřadit null
v jiném kódu.Jakákoliv referenční proměnná, která není nulová, má počáteční stav nenulový. Každá referenční proměnná s možnou hodnotou null má počáteční stav null, který má hodnotu možná-null.
Typ odkazu s možnou hodnotou null je zaznamenán pomocí stejné syntaxe jako typy hodnot null: ?
k typu proměnné se připojí. Například následující deklarace proměnné představuje proměnnou řetězce s možnou hodnotou null: name
string? name;
Pokud jsou povoleny odkazové typy s možnou hodnotou null, každá proměnná, která ?
není připojena k názvu typu, je nenulový odkazový typ. To zahrnuje všechny proměnné typu odkazu v existujícím kódu, jakmile tuto funkci povolíte. Všechny implicitně zadané místní proměnné (deklarované pomocí var
) jsou však odkazové typy s možnou hodnotou null. Jak ukázaly předchozí části, statická analýza určuje stav null místních proměnných, aby bylo možné určit, jestli jsou možná null před jeho dereferencací.
Někdy je nutné přepsat upozornění, když víte, že proměnná není null, ale kompilátor určí její stav null-null. Pomocí operátoru !
null-forgiving za názvem proměnné vynutíte, aby stav null nebyl null. Pokud například víte name
, že proměnná není null
, ale kompilátor vydá upozornění, můžete napsat následující kód, který přepíše analýzu kompilátoru:
name!.Length;
Odkazové typy s možnou hodnotou null a typy hodnot null poskytují podobný sémantický koncept: Proměnná může představovat hodnotu nebo objekt, nebo tato proměnná může být null
. Odkazové typy s možnou hodnotou null a typy hodnot null jsou však implementovány odlišně: typy hodnot s možnou hodnotou null jsou implementovány pomocí System.Nullable<T>a typy odkazů s možnou hodnotou null jsou implementovány atributy přečtenými kompilátorem. Například string?
oba string
jsou reprezentovány stejným typem: System.String.
int?
int
Jsou však reprezentovány System.Nullable<System.Int32>
a System.Int32v uvedeném pořadí.
Odkazové typy s možnou hodnotou null jsou funkcí doby kompilace. To znamená, že volající může ignorovat upozornění, záměrně použít null
jako argument metody, která očekává nenulový odkaz. Autoři knihoven by měli obsahovat kontroly za běhu s hodnotami argumentů null. Upřednostňovanou ArgumentNullException.ThrowIfNull možností je kontrola parametru proti hodnotě null za běhu. Kromě toho je chování programu při použití poznámek o možných hodnotách null stejné, i když jsou všechny tyto poznámky (?
a !
) odstraněny. Jediným účelem je vyjádřit záměr návrhu a poskytnout informace pro analýzu stavu null.
Důležité
Povolení poznámeksch Další podrobnosti najdete v článku o základech Entity Framework Core: Práce s odkazovými typy s možnou hodnotou Null.
Obecné typy vyžadují podrobná pravidla pro zpracování T?
libovolného parametru T
typu . Pravidla jsou nutně podrobná z důvodu historie a různé implementace pro typ hodnoty s možnou hodnotou null a typ odkazu s možnou hodnotou null.
Typy hodnot s možnou System.Nullable<T> hodnotou null jsou implementovány pomocí struktury.
Odkazové typy s možnou hodnotou null jsou implementovány jako poznámky typu, které kompilátoru poskytují sémantická pravidla.
T
typu typu odkaz, T?
odkazuje na odpovídající typ odkazu s možnou hodnotou null. Pokud je například T
, string
pak T?
je .string?
T
typu pro typ hodnoty, T?
odkazuje na stejný typ hodnoty . T
Pokud je například T
, int
T?
je to také int
.T
typu typu typu odkaz s možnou hodnotou null, T?
odkazuje na stejný typ odkazu s možnou hodnotou null. Pokud je například T
, string?
pak T?
je také string?
.T
typu pro typ T?
hodnoty null, odkazuje na stejný typ hodnoty nullable. Pokud je například T
, int?
pak T?
je také int?
.Pro návratové hodnoty T?
je ekvivalentní [MaybeNull]T
hodnotě ; pro hodnoty T?
argumentu je ekvivalentní [AllowNull]T
. Další informace najdete v článku o atributech pro analýzu stavu null v referenční dokumentaci jazyka.
Pomocí omezení můžete určit jiné chování:
class
znamená, že T
musí být nenulový odkazový typ (například string
). Kompilátor vygeneruje upozornění, pokud použijete odkaz s možnou hodnotou null, například string?
pro T
.class?
znamená, že T
musí být odkazovým typem, který nesmí být nullable (string
) nebo typu odkazu s možnou hodnotou null (například string?
). Pokud je parametr typu odkazem typu typu nullable, například string?
výraz T?
odkazů, které mají stejný typ odkazu s možnou hodnotou null, například string?
.notnull
znamená, že T
musí být nenulový odkazový typ nebo nenulový typ hodnoty. Pokud pro parametr typu použijete odkaz s možnou hodnotou null nebo typ hodnoty null, kompilátor vygeneruje upozornění. Pokud je to T
typ hodnoty, návratová hodnota je tento typ hodnoty, nikoli odpovídající typ hodnoty nullable.Tato omezení pomáhají kompilátoru poskytnout další informace o tom, jak T
se používá. To pomáhá, když vývojáři zvolí typ pro T
a poskytují lepší analýzu stavu null, když se použije instance obecného typu.
Kontext s možností typu null určuje, jak se zpracovávají poznámky referenčního typu s možností null a jaká upozornění vytváří analýza statického stavu možnosti null. Kontext s možnou hodnotou null obsahuje dva příznaky: nastavení poznámky
Nastavení poznámek
U malých projektů můžete povolit odkazové typy s možnou hodnotou null, opravit upozornění a pokračovat. U větších projektů a řešení s více projekty ale může vygenerovat velký počet upozornění. Pragmas můžete použít k povolení typů odkazů s možnou hodnotou null, když začnete používat odkazové typy s možnou hodnotou null. Nové funkce, které chrání před vyvoláním System.NullReferenceException , můžou být při zapnutí v existujícím základu kódu rušivé:
class
Kontext poznámek s možnou hodnotou null určuje chování kompilátoru. Existují čtyři kombinace nastavení u kontextu s možností hodnoty null:
?
k deklaraci typu odkazu s možnou hodnotou null vytvoří upozornění.!
ale nemá žádný vliv.?
můžete použít k deklaraci typu odkazu s možnou hodnotou null.?
přípony jsou odkazové typy bez hodnoty null.null
null
.
?
k deklaraci typu odkazu s možnou hodnotou null vytvoří upozornění.?
.!
.null
, nebo když přiřadíte výraz s možnou hodnotou null proměnné, která není null.
?
můžete použít k deklaraci typu odkazu s možnou hodnotou null.?
přípony jsou odkazové typy bez hodnoty null.!
ale nemá žádný vliv.Kontext poznámek s možnou hodnotou null a kontext upozornění s možnou hodnotou null lze nastavit pro projekt pomocí elementu <Nullable>
v souboru .csproj. Tento element konfiguruje, jak kompilátor interpretuje hodnotu nullability typů a jaká upozornění se vygenerují. Následující tabulka ukazuje povolené hodnoty a shrnuje kontexty, které zadávají.
Kontext | Upozornění na dereference | Upozornění přiřazení | Typy odkazů |
? přípona |
! operátor |
---|---|---|---|---|---|
disable |
Disabled | Disabled | Všechny mají možnou hodnotu null. | Vytvoří upozornění. | Nemá žádný vliv |
enable |
Povolený | Povolený | Bez hodnoty null, pokud není deklarováno ? |
Deklaruje typ s možnou hodnotou null. | Potlačí upozornění pro možné null přiřazení. |
warnings |
Povolený | Nelze použít | Všechny jsou nullable, ale členy jsou považovány za nenulové při otevření složené závorky metod. | Vytvoří upozornění. | Potlačí upozornění pro možné null přiřazení. |
annotations |
Disabled | Disabled | Bez hodnoty null, pokud není deklarováno ? |
Deklaruje typ s možnou hodnotou null. | Nemá žádný vliv |
Proměnné typu odkazu v kódu zkompilované v zakázaném kontextu jsou neplatné. Literál nebo proměnnou s možnou hodnotou null můžete přiřadit null
proměnné, která je neslušná. Výchozí stav proměnné s možnou hodnotou null však není null.
Můžete zvolit, které nastavení je pro váš projekt nejvhodnější:
Příklad:
<Nullable>enable</Nullable>
Direktivy můžete také použít k nastavení těchto stejných příznaků kdekoli ve zdrojovém kódu. Tyto direktivy jsou nejužitečnější při migraci velkého základu kódu.
#nullable enable
: Nastaví příznaky poznámek a upozornění na povolení.#nullable disable
: Nastaví příznaky poznámek a upozornění pro deaktivaci.#nullable restore
: Obnoví příznak poznámky a příznak upozornění do nastavení projektu.#nullable disable warnings
: Nastavte příznak upozornění na zakázat.#nullable enable warnings
: Nastavte příznak upozornění na pro aktivaci.#nullable restore warnings
: Obnoví příznak upozornění do nastavení projektu.#nullable disable annotations
: Nastavte příznak poznámek na zakázat.#nullable enable annotations
: Nastavte příznak poznámek na povolit.#nullable restore annotations
: Obnoví značku poznámky v nastavení projektu.Pro libovolný řádek kódu můžete nastavit některou z následujících kombinací:
Příznak upozornění | Příznak poznámky | Používání |
---|---|---|
Výchozí nastavení projektu | Výchozí nastavení projektu | Výchozí |
zapnout | disable | Oprava upozornění analýzy |
zapnout | Výchozí nastavení projektu | Oprava upozornění analýzy |
Výchozí nastavení projektu | zapnout | Přidání poznámek k typu |
zapnout | zapnout | Kód už migrovaný |
disable | zapnout | Označení kódu poznámkami před opravou upozornění |
disable | disable | Přidání staršího kódu do migrovaného projektu |
Výchozí nastavení projektu | disable | Zřídka |
disable | Výchozí nastavení projektu | Zřídka |
Tyto devět kombinací poskytují jemně odstupňovanou kontrolu nad diagnostikou, kterou kompilátor generuje pro váš kód. V jakékoli oblasti, kterou aktualizujete, můžete povolit další funkce, aniž byste viděli další upozornění, která ještě nejste připravení řešit.
Důležité
Globální kontext s možnou hodnotou null se nevztahuje na vygenerované soubory kódu. V obou strategiích je kontext s možnou hodnotou null zakázán pro jakýkoli zdrojový soubor označený jako vygenerovaný. To znamená, že žádná rozhraní API ve vygenerovaných souborech nejsou opatřena poznámkami. Pro vygenerované soubory se nevytvářejí žádná upozornění na možnost hodnoty null. Soubor je označený jako vygenerovaný čtyřmi způsoby:
generated_code = true
v oddílu, který se vztahuje na tento soubor.<auto-generated>
komentář do horní části souboru nebo <auto-generated/>
do komentáře. Může být na libovolném řádku v daném komentáři, ale blok komentáře musí být prvním prvkem v souboru.Generátory se můžou přihlásit pomocí direktivy preprocesoru #nullable
.
Ve výchozím nastavení jsou příznaky poznámek s možnou hodnotou null a upozornění zakázány. To znamená, že existující kód se zkompiluje beze změn a bez generování nových upozornění. Počínaje rozhraním .NET 6, nové projekty ve všech šablonách zahrnují prvek <Nullable>enable</Nullable>
, přičemž tyto příznaky jsou nastaveny na povoleno.
Tyto možnosti poskytují dvě odlišné strategie aktualizace existujícího základu kódu tak, aby používaly odkazové typy s možnou hodnotou null.
Pole a struktury, které obsahují odkazové typy, jsou známé nástrahy v odkazech s možnou hodnotou null a statická analýza, která určuje bezpečnost null. V obou situacích může být inicializován odkaz bez hodnoty null, aniž null
by se vygenerovaly upozornění.
Struktura, která obsahuje nenulové odkazové typy, umožňuje přiřazení default
bez upozornění. Představte si následující příklad:
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
V předchozím příkladu neexistuje žádné upozornění PrintStudent(default)
, zatímco odkazové typy FirstName
bez hodnoty null a LastName
mají hodnotu null.
Dalším běžným případem je, když se zabýváte obecnými strukturami. Představte si následující příklad:
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
V předchozím příkladu je Prop
vlastnost null
za běhu. Je přiřazený k nenulovatelnému řetězci bez upozornění.
Pole jsou také známým nástrahám v odkazových typech s možnou hodnotou null. Podívejte se na následující příklad, který nevygeneruje žádná upozornění:
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
V předchozím příkladu deklarace pole ukazuje, že obsahuje nenulové řetězce, zatímco jeho prvky jsou všechny inicializovány na null
. Potom je proměnná s
přiřazena null
hodnota (první prvek pole). Nakonec se proměnná s
dereferenced způsobuje výjimku za běhu.
Zpětná vazba k produktu .NET
.NET je open source projekt. Vyberte odkaz pro poskytnutí zpětné vazby:
Školení
Modul
Zabezpečení null v jazyce C# - Training
Naučte se kódovat postupy, které pomáhají zabránit výskytu NullReferenceException.