Události
Vytváření aplikací a agentů AI
17. 3. 21 - 21. 3. 10
Připojte se k řadě meetupů a vytvořte škálovatelná řešení AI založená na skutečných případech použití s kolegy vývojáři a odborníky.
ZaregistrovatTento prohlížeč se už nepodporuje.
Upgradujte na Microsoft Edge, abyste mohli využívat nejnovější funkce, aktualizace zabezpečení a technickou podporu.
Nulovatelné odkazové typy 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, který může být null, kompilátor vás upozorní. Proměnné, které jsou non-nullable, mají výchozí stav not-null.null
nebo výraz, který může být null. Pokud je stav proměnné možná-null, kompilátor vydá upozornění, pokud proměnnou dereferencujete. Výchozí stav null pro proměnnou je možná null.Zbytek tohoto článku popisuje, jak tyto tři oblasti funkcí fungují, aby vytvářely upozornění, když váš kód může dereferencovat hodnotu null
. Dereferencing proměnné znamená přístup k jednomu z její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 dereferencování proměnné, jejíž hodnota je null
, modul runtime vygeneruje System.NullReferenceException výjimku.
Podobně lze upozornění vytvořit, když je zápis []
použit pro přístup k členu objektu, když je objekt null
.
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 dereference 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#.
Analýza nulového stavu sleduje nulový stav referencí. Výraz buď není null, nebo možná null. Kompilátor určuje, že proměnná není null dvěma způsoby:
null
a od té doby nebyla přiřazena.Každá proměnná, kterou kompilátor nemůže určit jako nenulová, se považuje za možná nulová. Analýza poskytuje upozornění v situacích, kdy byste mohli omylem dereferencovat hodnotu null
. Kompilátor generuje upozornění na základě null-stavu.
null
před dereferencová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
je možná null při tisku první zprávy. 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ů ke kořeni stromu a zpracování každého uzlu 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
není nikdy dereferencován, pokud může být null. Proměnná current
se porovnává s null
před přístupem k current.Parent
, a než se předá current
akci ProcessNode
. 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ími pro nullable typy poskytuje techniky pro opravu upozornění, která pravděpodobně vidíte ve svém kódu. 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ění stav null proměnné z možná null na není 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í null-stavmessage
na not-null uvnitř if (!IsNull(message)) {...}
bloku. Nejsou vydána žádná upozornění.
Atributy poskytují podrobné informace o nullovém stavu argumentů, návratových hodnot a členů instance objektu použitých k vyvolání členu. Podrobnosti o jednotlivých atributech najdete v referenčním článku jazyka o nullable referenčních atributech. 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 nulového stavu 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 někdy mohlo být členské pole nastaveno na null
, musí kompilátor předpokládat, že jeho nullový stav je možná-null na začátku každé metody.
Používáte poznámky, které mohou deklarovat, zda je proměnná s odkazovým typem, který může nabývat hodnoty null, nebo s odkazovým typem, který nemůže nabývat hodnoty null. 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 jako možná-null.
Typ nulovatelného odkazu je označen pomocí stejné syntaxe jako nulovatelné hodnotové typy: 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 nulovatelné odkazové typy, každá proměnná, ke které není název typu doplněn ?
, je nenulový odkazový typ. To zahrnuje všechny proměnné typu odkazu v existujícím kódu, jakmile tuto funkci povolíte. Nicméně, všechny implicitně zadané místní proměnné (deklarované pomocí var
) jsou nulovatelné odkazové typy. Jak ukázaly předchozí části, statická analýza určuje null-stav místních proměnných, aby určila, jestli jsou možná nullové před jejich dereferencací.
Někdy je nutné přepsat upozornění, když víte, že proměnná není null, ale kompilátor určí její stav null jako možná null. Pomocí operátoru null-forgiving za názvem proměnné vynutíte, že stav null bude považován za nenulový. Například pokud víte, že proměnná name
není null
, ale kompilátor vydá upozornění, můžete napsat následující kód, kterým přepíšete 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. Avšak int?
a int
jsou reprezentovány System.Nullable<System.Int32>
a System.Int32, respektive.
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 zahrnout kontroly prováděné za běhu proti hodnotám 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ámek o nulovatelnosti může změnit způsob, jakým Entity Framework Core určuje, zda je datový prvek povinný. Další podrobnosti najdete v článku o základech Entity Framework Core: Práce s nulovatelnými referenčními typy.
Obecné typy vyžadují podrobná pravidla pro zpracování T?
libovolného parametru T
typu . Pravidla jsou nutně podrobná kvůli historii a různé implementaci pro nulovatelný hodnotový typ a nulovatelný referenční typ.
Nulovatelné datové typy jsou implementovány pomocí struktury System.Nullable<T>.
Odkazové typy s možnou hodnotou null jsou implementovány jako poznámky typu, které kompilátoru poskytují sémantická pravidla.
T
odkazový typ, T?
odkazuje na odpovídající odkazový typ s možností null. Například, pokud je T
string
, pak T?
je string?
.T
typem hodnoty, T?
odkazuje na stejný typ hodnotyT
. Pokud je například T
int
, pak T?
je také int
.T
odkazového typu s možnou hodnotou null, T?
odkazuje na stejný odkazový typ s možnou hodnotou null. Pokud je například T
string?
, pak T?
je také string?
.T
nulovatelný typ hodnoty, T?
odkazuje na tentýž nulovatelný typ hodnoty. Pokud je například T
int?
, pak T?
je také int?
.Pro návratové hodnoty je T?
ekvivalentní s [MaybeNull]T
; pro hodnoty argumentu je T?
ekvivalentní s [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, buď nenulovatelným typem odkazu (string
), nebo typem odkazu s možnou hodnotou null (například string?
). Pokud je parametr typu odkazem typu nullable, například string?
, výraz T?
, který odkazuje na stejný nullable referenční typ, například string?
.notnull
znamená, že T
musí být nenulový odkazový typ nebo nenulový typ hodnoty. Pokud pro parametr typu použijete nulovatelný referenční typ nebo nulovatelný hodnotový typ, 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 poskytuje lepší analýzu stavu null, když je použita 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í anotační a nastavení výstrahy.
Nastavení poznámek i upozornění jsou u stávajících projektů ve výchozím stavu zakázaná. Počínaje verzí .NET 6 (C# 10) jsou oba příznaky ve výchozím nastavení zapnuty pro nové projekty. Důvodem dvou odlišných příznaků pro nullable kontext je zjednodušit migraci velkých projektů, které existují před zavedením nullable referenčních typů.
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í. Můžete použít pragmas k povolení odkazových typů s možnou hodnotou null po jednotlivých souborech, jakmile začnete používat nullable reference types. 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
v obecných typech se změnil tak, že nyní znamená nenulový referenční typ.Kontext poznámek s možnou hodnotou null určuje chování kompilátoru. Existují čtyři kombinace nastavení u kontextu s hodnotou 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.?
suffix are non-nullable reference types.
Proměnné typu odkazu bez přípony ?
jsou odkazové typy nepovolující hodnotu null.!
ale nemá žádný vliv.Kontext anotace null a kontext varování null lze nastavit pro projekt pomocí elementu <Nullable>
ve vašem 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 |
Deaktivováno | Zakázáno | Všechny jsou nulovatelné. | Vyvolá upozornění. | Nemá žádný vliv |
enable |
Povoleno | Povoleno | Nenulové, pokud není deklarováno pomocí ? |
Deklaruje typ s možnou hodnotou null. | Potlačí možná upozornění při přiřazení null . |
warnings |
Povoleno | Nelze použít | Všechny jsou nullable, ale členy jsou považovány za nenulové při otevření složené závorky metod. | Vyvolá upozornění. | Potlačí upozornění na možné null přiřazení. |
annotations |
Neaktivní | Zakázáno | Nelze nastavit na hodnotu null, pokud není deklarováno s ? |
Deklaruje nulovatelný typ. | Nemá žádný vliv |
Proměnné typu odkazu v kódu zkompilované v zakázaném kontextu jsou neplatné. Literál null
nebo proměnnou s možnou hodnotou null můžete přiřadit proměnné, která je zamlžená. 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 povoleno.#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 | zakázat | Odstraňte varování analýzy |
povolit | Výchozí nastavení projektu | Opravit upozornění analýzy |
Výchozí nastavení projektu | povolit | Přidání typových anotací |
povolit | umožnit | Kód už migrovaný |
zakázat | povolit | Označte kód poznámkami před opravou upozornění |
zakázat | zakázat | Přidání staršího kódu do migrovaného projektu |
Výchozí nastavení projektu | zakázat | Zřídka |
zakázat | 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>
nebo <auto-generated/>
do komentáře v horní části souboru. 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 nepovolitelný odkaz inicializován na null
, aniž by se vygenerovalo 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 není žádné upozornění v PrintStudent(default)
, zatímco nenulovatelné odkazové typy FirstName
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;
}
}
Za běhu je v předchozím příkladu vlastnost Prop
null
. Je přiřazený k nenulovatelnému řetězci bez upozornění.
Pole jsou také známým problémem v nullable reference types. 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, když je proměnná s
dereferencována, způsobí běhovou výjimku.
Konstruktor třídy bude stále volat finalizátor, i když došlo k výjimce vyvolané tímto konstruktorem.
Následující příklad ukazuje toto chování:
public class A
{
private string _name;
private B _b;
public A(string name)
{
ArgumentNullException.ThrowIfNullOrEmpty(name);
_name = name;
_b = new B();
}
~A()
{
Dispose();
}
public void Dispose()
{
_b.Dispose();
GC.SuppressFinalize(this);
}
}
public class B: IDisposable
{
public void Dispose() { }
}
public void Main()
{
var a = new A(string.Empty);
}
V předchozím příkladu bude System.NullReferenceException vyvoláno při spuštění _b.Dispose();
, pokud byl parametr name
null
. Volání _b.Dispose();
nikdy nevyvolá výjimku, pokud se konstruktor dokončí úspěšně. Kompilátor ale nevystavil žádné upozornění, protože statická analýza nemůže určit, jestli se metoda (jako konstruktor) dokončí bez vyvolání výjimky za běhu.
Zpětná vazba k produktu .NET
.NET je open source projekt. Vyberte odkaz pro poskytnutí zpětné vazby:
Události
Vytváření aplikací a agentů AI
17. 3. 21 - 21. 3. 10
Připojte se k řadě meetupů a vytvořte škálovatelná řešení AI založená na skutečných případech použití s kolegy vývojáři a odborníky.
ZaregistrovatŠkolení
Modul
Zabezpečení null v jazyce C# - Training
Naučte se kódovat postupy, které pomáhají zabránit výskytu NullReferenceException.