Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Null értékű környezetben a fordító statikus kódelemzést végez az összes referenciatípus-változó nullállapotának meghatározásához:
- not-null: A statikus elemzés azt határozza meg, hogy egy változó nem null értékű-e.
- maybe-null: A statikus elemzés nem tudja megállapítani, hogy egy változó nem null értékhez van-e hozzárendelve.
Ezek az állapotok lehetővé teszik a fordító számára, hogy figyelmeztetéseket adjon meg, amikor null értéket halaszthat el, és egy System.NullReferenceException. Ezek az attribútumok szemantikai információkat nyújtanak a fordítónak az argumentumok null állapotáról , az értékek visszaadásáról és az objektumtagokról. Az attribútumok tisztázzák az argumentumok állapotát, és visszaadják az értékeket. A fordító pontosabb figyelmeztetéseket biztosít, ha az API-k megfelelően vannak megjelölve ezekkel a szemantikai információkkal.
Ez a cikk röviden ismerteti az egyes null értékű hivatkozástípus-attribútumokat és azok használatát.
Kezdjük egy példával. Tegyük fel, hogy a kódtár a következő API-val rendelkezik, amely lekéri az erőforrás-sztringet. Ezt a metódust eredetileg null értékű, oblivious környezetben állították össze:
bool TryGetMessage(string key, out string message)
{
if (_messageMap.ContainsKey(key))
message = _messageMap[key];
else
message = null;
return message != null;
}
Az előző példa a .NET ismerős Try* mintáját követi. Ennek az API-nak két referenciaparamétere van: az key és a message. Ennek az API-nak a következő szabályai vannak a paraméterek nullállapotára vonatkozóan:
- A hívók nem adhatják meg
nullargumentumként a következőtkey: . - A hívók olyan változót adhatnak át,
nullamelynek értéke argumentumként szerepelmessage. - Ha a
TryGetMessagemetódus eredményül adtruevissza, a metódus értékemessagenem null. Ha a visszatérési érték nullfalseértékű.message
A szabály key tömören kifejezhető: key nem null értékű referenciatípusnak kell lennie. A message paraméter összetettebb. Lehetővé teszi egy argumentumként megadott null változót, de garantálja, hogy az out argumentum nem nullaz . Ezekben a forgatókönyvekben gazdagabb szókincsre van szükség az elvárások leírásához. Az NotNullWhen attribútum a paraméterhez használt argumentum nullállapotátmessage írja le.
Feljegyzés
Az attribútumok hozzáadása további információkat nyújt a fordítónak az API szabályairól. Ha a kód hívása null értékű, engedélyezett környezetben történik, a fordító figyelmezteti a hívókat, ha megsértik ezeket a szabályokat. Ezek az attribútumok nem teszik lehetővé a megvalósítás további ellenőrzését.
| Attribútum | Kategória | Értelmezés |
|---|---|---|
| AllowNull | Előfeltétel | Lehet, hogy egy nem null értékű paraméter, mező vagy tulajdonság null értékű. |
| DisallowNull | Előfeltétel | A null értékű paraméter, mező vagy tulajdonság soha nem lehet null értékű. |
| MaybeNull | Utókondíció | Egy nem null értékű paraméter, mező, tulajdonság vagy visszatérési érték null értékű lehet. |
| NotNull | Utókondíció | A null értékű paraméter, mező, tulajdonság vagy visszatérési érték soha nem null értékű. |
| MaybeNullWhen | Feltételes utókondíció | A nem null értékű argumentum null értékű lehet, ha a metódus a megadott bool értéket adja vissza. |
| NotNullWhen | Feltételes utókondíció | A null értékű argumentum nem null értékű, ha a metódus a megadott bool értéket adja vissza. |
| NotNullIfNotNull | Feltételes utókondíció | A visszaadott érték, tulajdonság vagy argumentum nem null értékű, ha a megadott paraméter argumentuma nem null. |
| MemberNotNull | Metódus- és tulajdonságsegítő módszerek | A listában szereplő tag nem null értékű, amikor a metódus visszatér. |
| MemberNotNullWhen | Metódus- és tulajdonságsegítő módszerek | A listában szereplő tag nem null értékű, ha a metódus a megadott bool értéket adja vissza. |
| DoesNotReturn | Nem elérhető kód | Egy metódus vagy tulajdonság soha nem ad vissza. Más szóval, ez mindig kivételt vet ki. |
| DoesNotReturnIf | Nem elérhető kód | Ez a metódus vagy tulajdonság soha nem ad vissza értéket, ha a társított bool paraméter a megadott értékkel rendelkezik. |
Az előző leírások rövid áttekintést adnak arról, hogy az egyes attribútumok mit is végeznek. Az alábbi szakaszok részletesebben ismertetik ezeknek az attribútumoknak a viselkedését és jelentését.
Előfeltételek: AllowNull és DisallowNull
Fontolja meg egy olyan olvasási/írási null tulajdonságot, amely soha nem ad vissza, mert ésszerű alapértelmezett értékkel rendelkezik. A hívók az alapértelmezett értékre való beállításkor adják át null a hívóknak a beállított tartozékot. Vegyük például azt az üzenetküldő rendszert, amely egy csevegőszobában kéri a képernyő nevét. Ha nincs megadva, a rendszer véletlenszerű nevet hoz létre:
public string ScreenName
{
get => _screenName;
set => _screenName = value ?? GenerateRandomScreenName();
}
private string _screenName;
Ha az előző kódot null értékű, oblivious környezetben fordítja le, minden rendben van. A null értékű hivatkozástípusok engedélyezése után a ScreenName tulajdonság nem null értékű hivatkozássá válik. A hívóknak nem kell ellenőrizni a visszaadott tulajdonságot null. Most azonban a tulajdonság null beállítása figyelmeztetést eredményez. Az ilyen típusú kód támogatásához adja hozzá az System.Diagnostics.CodeAnalysis.AllowNullAttribute attribútumot a tulajdonsághoz az alábbi kódban látható módon:
[AllowNull]
public string ScreenName
{
get => _screenName;
set => _screenName = value ?? GenerateRandomScreenName();
}
private string _screenName = GenerateRandomScreenName();
Előfordulhat, hogy hozzá kell adnia egy using irányelvet System.Diagnostics.CodeAnalysis ennek és a cikkben tárgyalt egyéb attribútumoknak a használatához. Az attribútum nem a tartozékra, hanem a tulajdonságra van alkalmazva set . Az AllowNull attribútum előfeltételeket határoz meg, és csak az argumentumokra vonatkozik. A get tartozék visszatérési értékkel rendelkezik, de nincsenek paraméterek. Ezért az AllowNull attribútum csak a set tartozékra vonatkozik.
Az előző példa bemutatja, mit kell keresni az AllowNull attribútum argumentumhoz való hozzáadásakor:
- A változó általános szerződése, hogy nem lehet
null, ezért nem null értékű referenciatípust szeretne. - Vannak olyan forgatókönyvek, amikor a hívó argumentumként adja át
nulla hívót, bár nem ez a leggyakoribb használat.
Ez az attribútum leggyakrabban tulajdonságokhoz vagy in, outés ref argumentumokhoz szükséges. Az AllowNull attribútum a legjobb választás, ha egy változó általában nem null értékű, de előfeltételként engedélyeznie null kell.
Ellentétben a következő forgatókönyvekkel DisallowNull: Ezzel az attribútummal megadhatja, hogy egy null értékű hivatkozástípus argumentuma ne legyen null. Fontolja meg az alapértelmezett értéknek számító tulajdonságot null , de az ügyfelek csak nem null értékűre állíthatják be. Tekintse meg az alábbi kódot:
public string ReviewComment
{
get => _comment;
set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
string _comment;
Az előző kód a legjobb módja annak, hogy kifejezze a tervét, amely ReviewComment lehet null, de nem állítható be null. Ha ez a kód null értékű, a következővel egyértelműbbé teheti ezt a fogalmat a System.Diagnostics.CodeAnalysis.DisallowNullAttributehívóknak:
[DisallowNull]
public string? ReviewComment
{
get => _comment;
set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
string? _comment;
Null értékű környezetben a ReviewCommentget tartozék visszaadhatja az alapértelmezett értéket null. A fordító figyelmezteti, hogy a hozzáférés előtt ellenőrizni kell. Ezenkívül figyelmezteti a hívókat, hogy bár lehetséges null, a hívónak nem szabad explicit módon beállítania null. Az DisallowNull attribútum egy előfeltételt is megad, nem befolyásolja a tartozékotget. Az attribútumot akkor DisallowNull használja, amikor az alábbi jellemzőket figyeli meg:
- A változó alapforgatókönyvekben is szerepelhet
null, gyakran az első példányosításkor. - A változót nem szabad explicit módon beállítani
null.
Ezek a helyzetek gyakoriak az eredetileg null értékű kódban. Előfordulhat, hogy az objektumtulajdonságok két különböző inicializálási műveletben vannak beállítva. Előfordulhat, hogy egyes tulajdonságok csak az aszinkron munka befejezése után vannak beállítva.
Az AllowNull és DisallowNull az attribútumok lehetővé teszik annak megadását, hogy a változók előfeltételei nem feltétlenül egyeznek-e a változók null értékű széljegyzetével. Ezek a széljegyzetek részletesebben ismertetik az API jellemzőit. Ez a további információ segít a hívóknak az API helyes használatát. Ne feledje, hogy az alábbi attribútumokkal határoz meg előfeltételeket:
- AllowNull: Lehet, hogy egy nem null értékű argumentum null értékű.
- DisallowNull: A null értékű argumentumnak soha nem szabad null értékűnek lennie.
Utókondíciók: MaybeNull és NotNull
Tegyük fel, hogy rendelkezik a következő aláírással rendelkező metódussal:
public Customer FindCustomer(string lastName, string firstName)
Valószínűleg egy ilyen metódust írt vissza null , amikor a keresett név nem található. Az null egyértelműen jelzi, hogy a rekord nem található. Ebben a példában valószínűleg a visszatérési típust Customer a következőre Customer?módosítja: . A visszatérési érték null értékű hivatkozástípusként való deklarálása egyértelműen meghatározza az API szándékát:
public Customer? FindCustomer(string lastName, string firstName)
A Generics nullabilitása miatt előfordulhat, hogy ez a technika nem hozza létre az API-nak megfelelő statikus elemzést. Lehet, hogy egy hasonló mintát követő általános metódussal rendelkezik:
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
A metódus akkor ad vissza, null ha a keresett elem nem található. Egyértelművé teheti, hogy a metódus akkor ad vissza értéket null , ha egy elem nem található, ha hozzáadja a MaybeNull megjegyzést a metódus visszatéréséhez:
[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate)
Az előző kód tájékoztatja a hívókat, hogy a visszatérési érték valójában null lehet . Arról is tájékoztatja a fordítót, hogy a metódus akkor is visszaadhat egy null kifejezést, ha a típus nem null értékű. Ha olyan általános metódussal rendelkezik, amely a típusparaméter egy példányát adja vissza, kifejezheti, Thogy az attribútum használatával null soha nem tér visszaNotNull.
Azt is megadhatja, hogy egy visszaadott érték vagy argumentum ne legyen null értékű, annak ellenére, hogy a típus null értékű hivatkozástípus. Az alábbi módszer egy segédmetódus, amely akkor dob, ha az első argumentuma a következő null:
public static void ThrowWhenNull(object value, string valueExpression = "")
{
if (value is null) throw new ArgumentNullException(nameof(value), valueExpression);
}
Ezt a rutint a következőképpen hívhatja meg:
public static void LogMessage(string? message)
{
ThrowWhenNull(message, $"{nameof(message)} must not be null");
Console.WriteLine(message.Length);
}
A nullhivatkozás-típusok engedélyezése után meg kell győződnie arról, hogy az előző kód figyelmeztetések nélkül fordítható le. A metódus visszatérése esetén a value paraméter garantáltan nem null értékű. Elfogadható azonban null hivatkozással meghívni ThrowWhenNull .
value Létrehozhat egy null értékű hivatkozástípust, és hozzáadhatja az NotNull utólagos feltételt a paraméterdeklarációhoz:
public static void ThrowWhenNull([NotNull] object? value, string valueExpression = "")
{
_ = value ?? throw new ArgumentNullException(nameof(value), valueExpression);
// other logic elided
Az előző kód egyértelműen kifejezi a meglévő szerződést: A hívók átadhatnak egy változót az null értékkel, de az argumentum garantáltan soha nem lesz null, ha a metódus kivétel nélkül tér vissza.
Feltétel nélküli utókondíciókat a következő attribútumokkal adhat meg:
- MaybeNull: A nem null értékű visszatérési érték null értékű lehet.
- NotNull: A null értékű visszatérési érték soha nem null.
Feltételes utófeltételek: NotNullWhen, MaybeNullWhenés NotNullIfNotNull
Valószínűleg ismeri a metódust stringString.IsNullOrEmpty(String). Ez a metódus null értékű vagy üres sztring esetén ad vissza true értéket. Ez a null-ellenőrzés egyik formája: A hívóknak nem kell null-ellenőrzéssel ellenőrizni az argumentumot false, ha a metódus visszatér. Ha egy ilyen null értékű metódust szeretne felismerni, állítsa az argumentumot null értékű hivatkozástípusra, és adja hozzá az NotNullWhen attribútumot:
bool IsNullOrEmpty([NotNullWhen(false)] string? value)
Ez tájékoztatja a fordítót arról, hogy a visszatérési értéket false tartalmazó kódoknak nincs szükségük null értékű ellenőrzésekre. Az attribútum hozzáadása tájékoztatja a fordító statikus elemzését, amely IsNullOrEmpty elvégzi a szükséges null ellenőrzést: amikor visszaadja false, az argumentum nem null.
string? userInput = GetUserInput();
if (!string.IsNullOrEmpty(userInput))
{
int messageLength = userInput.Length; // no null check needed.
}
// null check needed on userInput here.
A String.IsNullOrEmpty(String) metódus az előző példában látható módon történik. Előfordulhat, hogy a kódbázisban hasonló metódusok vannak, amelyek ellenőrzik az objektumok állapotát null értékek esetén. A fordító nem ismeri fel az egyéni nullellenőrzési módszereket, és önnek kell hozzáadnia a széljegyzeteket. Az attribútum hozzáadásakor a fordító statikus elemzése tudja, hogy a tesztelt változó mikor van null-ellenőrzéssel ellenőrizve.
Ezeknek az attribútumoknak egy másik felhasználási módja a Try* minta. A rendszer a visszatérési értéken keresztül közli a postconditions és refout az argumentumokat. Fontolja meg ezt a korábban bemutatott módszert (null értékű letiltott környezetben):
bool TryGetMessage(string key, out string message)
{
if (_messageMap.ContainsKey(key))
message = _messageMap[key];
else
message = null;
return message != null;
}
Az előző metódus egy tipikus .NET-idiómát követ: a visszatérési érték azt jelzi, hogy message a keresett értékre vagy , ha nem található üzenet, az alapértelmezett értékre van-e beállítva. Ha a metódus eredményül ad truevissza, a metódus értéke message nem null, ellenkező esetben a metódus null értékre van adva message .
Null értékű, engedélyezett környezetben az attribútum használatával kommunikálhatja ezt az NotNullWhen idiómát. Ha a null értékű hivatkozástípusok paramétereit jegyzeteli, hozzon létre message és string? adjon hozzá egy attribútumot:
bool TryGetMessage(string key, [NotNullWhen(true)] out string? message)
{
if (_messageMap.ContainsKey(key))
message = _messageMap[key];
else
message = null;
return message is not null;
}
Az előző példában a visszaadott érték message nem null TryGetMessagetrueértékű. Hasonló metódusokat ugyanúgy kell jegyzetelni a kódbázisban: az argumentumok egyenlők nulllehetnek, és a metódus visszatérésekor truenem null értékűek.
Egy utolsó attribútumra is szüksége lehet. Előfordulhat, hogy egy visszatérési érték null állapota egy vagy több argumentum null állapotától függ. Ezek a metódusok nem null értéket adnak vissza, ha bizonyos argumentumok nem null. A metódusok helyes megjegyzéséhez használja az NotNullIfNotNull attribútumot. Fontolja meg a következő módszert:
string GetTopLevelDomainFromFullUrl(string url)
Ha az url argumentum nem null értékű, a kimenet nem null. A null értékű hivatkozások engedélyezése után további széljegyzeteket kell hozzáadnia, ha az API elfogad egy null argumentumot. A visszatérési típust a következő kódban látható módon fűzheti megjegyzéshez:
string? GetTopLevelDomainFromFullUrl(string? url)
Ez is működik, de gyakran kényszeríti a hívókat, hogy további null ellenőrzéseket hajtsanak végre. A szerződés az, hogy a visszatérési érték csak akkor lesznull, ha az argumentum url .null A szerződés kifejezéséhez a következő kódban látható módon kell megjegyzést fűznie ehhez a módszerhez:
[return: NotNullIfNotNull(nameof(url))]
string? GetTopLevelDomainFromFullUrl(string? url)
Az előző példa az operátort nameof használja a paraméterhez url. A visszatérési érték és az argumentum is széljegyzetet kapott azzal a ? jelzéssel, hogy bármelyik lehet null. Az attribútum azt is egyértelművé teszi, hogy a visszatérési érték nem null értékű, ha az url argumentum nem null.
A feltételes utókondíciókat az alábbi attribútumokkal adhatja meg:
-
MaybeNullWhen: A nem null értékű argumentum null értékű lehet, ha a metódus a megadott
boolértéket adja vissza. -
NotNullWhen: A null értékű argumentum nem null értékű, ha a metódus a megadott
boolértéket adja vissza. - NotNullIfNotNull: A visszatérési érték nem null, ha a megadott paraméter argumentuma nem null.
Segítő módszerek: MemberNotNull és MemberNotNullWhen
Ezek az attribútumok határozzák meg a szándékot, amikor a konstruktoroktól a segédmeteorokká alakítja át a közös kódot. A C#-fordító elemzi a konstruktorokat és a mező inicializálóit, hogy minden nem null értékű referenciamező inicializálva legyen az egyes konstruktorok visszatérése előtt. A C#-fordító azonban nem követi nyomon a mezőhozzárendeléseket az összes segédmeteképpen. A fordító figyelmeztetést CS8618 ad ki, ha a mezőket nem közvetlenül a konstruktorban inicializálják, hanem segédmetódusban. Adja hozzá a MemberNotNullAttribute metódusdeklarációhoz, és adja meg a metódus nem null értékére inicializált mezőket. Vegyük például a következő példát:
public class Container
{
private string _uniqueIdentifier; // must be initialized.
private string? _optionalMessage;
public Container()
{
Helper();
}
public Container(string message)
{
Helper();
_optionalMessage = message;
}
[MemberNotNull(nameof(_uniqueIdentifier))]
private void Helper()
{
_uniqueIdentifier = DateTime.Now.Ticks.ToString();
}
}
Az attribútumkonstruktor MemberNotNull argumentumaként több mezőnevet is megadhat.
Ennek MemberNotNullWhenAttribute argumentuma van bool . Olyan helyzetekben használja MemberNotNullWhen , amikor a segédmetódus azt bool jelzi, hogy a segédmetódus inicializált-e mezőket.
Null értékű elemzés leállítása metódusdobáskor
Egyes metódusok, jellemzően kivételsegítők vagy egyéb segédprogramok mindig kivétel kivetésével lépnek ki. Vagy egy segítő kivételt jelez egy logikai argumentum értéke alapján.
Az első esetben hozzáadhatja az attribútumot a DoesNotReturnAttribute metódusdeklarációhoz. A fordító nullállapot-elemzése nem ellenőrzi a metódus olyan kódját, amely egy olyan metódus meghívását követi, amely a következővel DoesNotReturnvan eljegyzve: . Fontolja meg ezt a módszert:
[DoesNotReturn]
private void FailFast()
{
throw new InvalidOperationException();
}
public void SetState(object containedField)
{
if (containedField is null)
{
FailFast();
}
// containedField can't be null:
_field = containedField;
}
A fordító nem ad ki figyelmeztetést a hívás FailFastután.
A második esetben hozzáadja az attribútumot a System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute metódus logikai paraméteréhez. Az előző példát az alábbiak szerint módosíthatja:
private void FailFastIf([DoesNotReturnIf(true)] bool isNull)
{
if (isNull)
{
throw new InvalidOperationException();
}
}
public void SetFieldState(object? containedField)
{
FailFastIf(containedField == null);
// No warning: containedField can't be null here:
_field = containedField;
}
Ha az argumentum értéke megegyezik a DoesNotReturnIf konstruktor értékével, a fordító nem végez nullállapot-elemzést a metódus után.
Összegzés
Null értékű referenciatípusok hozzáadása kezdeti szókészletet biztosít az API-k változókkal nullkapcsolatos elvárásainak leírásához. Az attribútumok gazdagabb szókészletet biztosítanak a változók null állapotának előfeltételként és utókondícióként való leírásához. Ezek az attribútumok jobban leírják az elvárásait, és jobb élményt nyújtanak az API-kat használó fejlesztők számára.
Amikor null értékű környezet kódtárait frissíti, adja hozzá ezeket az attribútumokat, hogy az API-k felhasználói a megfelelő használathoz vezessenek. Ezek az attribútumok segítenek az argumentumok null állapotának teljes leírásában és az értékek visszaadásában.
- AllowNull: Lehet, hogy egy nem null értékű mező, paraméter vagy tulajdonság null értékű.
- DisallowNull: A null értékű mező, paraméter vagy tulajdonság soha nem lehet null értékű.
- MaybeNull: Lehet, hogy egy nem null értékű mező, paraméter, tulajdonság vagy visszatérési érték null értékű.
- NotNull: A null értékű mező, paraméter, tulajdonság vagy visszatérési érték soha nem null.
-
MaybeNullWhen: A nem null értékű argumentum null értékű lehet, ha a metódus a megadott
boolértéket adja vissza. -
NotNullWhen: A null értékű argumentum nem null értékű, ha a metódus a megadott
boolértéket adja vissza. - NotNullIfNotNull: A paraméter, tulajdonság vagy visszatérési érték nem null, ha a megadott paraméter argumentuma nem null.
- DoesNotReturn: Egy metódus vagy tulajdonság soha nem ad vissza. Más szóval, ez mindig kivételt vet ki.
-
DoesNotReturnIf: Ez a metódus vagy tulajdonság soha nem ad vissza értéket, ha a társított
boolparaméter a megadott értékkel rendelkezik.