Nebezpečný vývoj

Problém šampiona: https://github.com/dotnet/csharplang/issues/9704

Shrnutí

Aktualizujeme definici jazyka unsafe C# tak, aby odkazovala na umístění, kde se používají typy ukazatelů, na umístění, kde modul runtime nespravuje paměť, která nespravuje. Tato umístění jsou místem, kde dochází k nezabezpečené paměti a zodpovídají za velké množství cves (běžné ohrožení zabezpečení a ohrožení zabezpečení) zařazených do kategorií jako problémy s bezpečností paměti.

// Under the proposed rules:
void M()
{
    int i = 1;
    int* ptr = &i; // Allowed: creating a pointer is not itself unsafe
    unsafe
    {
        Console.WriteLine(*ptr); // Dereference of memory not managed by the runtime. This is unsafe.
        ref int intRef = Unsafe.AsRef(ptr); // Conversion of memory not managed by the runtime to a `ref`. This is unsafe.
    }
}

namespace System.Runtime.CompilerServices
{
    public static class Unsafe
    {
        unsafe public static ref T AsRef<T>(void* source) { /* ... */ } // `unsafe` marks the member as *requires-unsafe*.
    }
}

Motivation

Na pozadí této funkce najdete https://github.com/dotnet/designs/blob/main/accepted/2025/memory-safety/caller-unsafe.mdtaké informace o širších změnách ekosystému, které budou v rámci tohoto návrhu potřeba. Patří mezi ně aktualizace seznamu BCL pro správné přidávání poznámek k nezabezpečeným metodám a také aktualizace nástrojů pro lepší pochopení místa, kde dochází k nebezpečné paměti. Pro jazyk C# chceme zajistit, aby jazyk správně sledoval nezabezpečenou paměť; dnes může být obtížné podívat se na program holisticky a pochopit všechna místa, kde dochází k nebezpečné paměti. Je to proto, že různí pomocní lidé, jako System.Runtime.CompilerServices.Unsafeje , System.Runtime.InteropServices.Marshala jiní nevyjádřují, že porušují bezpečnost paměti a potřebují zvláštní pozornost. Metody, které pak tyto pomocné rutiny používají, nejsou okamžitě zřejmé a při auditování kódu pro problémy s bezpečností paměti (předem při kontrole nebo při pokusu o zjištění příčiny nahlášené chyby zabezpečení) může být obtížné určit umístění, která by mohla přispívat k problémům.

V jazyce C# se historicky unsafe odkazuje na konkrétní bezpečnostní díru paměti: existence typů ukazatelů. V okamžiku, kdy už není zapojen typ ukazatele, je jazyk C# dokonale šťastný, když necháte paměť nebezpečnou lehnout v kódu. Jedná se o tento problém, který se snažíme vyřešit s tímto vývojem unsafe v jazyce C# a ekosystému .NET, označování oblastí, ve kterých by mohlo dojít k nebezpečné paměti, což usnadňuje kontrolorům a auditorům pochopení hranic potenciálního nebezpečného paměti v programu. Důležité je, že to znamená, že změníme význam unsafe, nikoli jen jeho rozšíření. Existence ukazatele není sama o sobě nebezpečná; nebezpečná akce přemístí ukazatel. To se dále rozšiřuje na samotné typy; typy nemohou být ze své podstaty nebezpečné. Je to pouze akce použití typu, která by mohla být nebezpečná, nikoli existence tohoto typu.

Abychom mohli tyto informace protékat systémem, musíme tedy mít způsob, jak označovat metody jako unsafe. Dnes, unsafe protože modifikátor metody nemá žádný vnější dopad, umožňuje použít pouze ukazatele v podpisu a textu členu. V budoucnu unsafe , protože modifikátor skutečně veřejně změní význam člena; bude indikovat, že člen má obavy o bezpečnost paměti a veškeré využití musí být ručně ověřeno programátorem pomocí člena. Toto je rozšíření stávajícího významu unsafe: unsafe v subjektu lokalizuje povinnost auditu tomuto subjektu, zatímco unsafe podpis rozšiřuje tuto povinnost volajícímu.

Jedná se o potenciálně velkou zásadní změnu pro konkrétní segmenty uživatelské základny jazyka C#. Doufáme, že pro mnoho našich uživatelů je to v podstatě transparentní a aktualizace nových pravidel bude bezproblémová. Vzhledem k tomu, že některé velké plochy rozhraní API, jako jsou velké části odrazu, však mohou být označeny unsafe, myslíme si, že pravděpodobně bude nutné, aby nová pravidla byla slušná, aby se zabránilo úplně rozfoukání ekosystému.

Zásadní změny

Při aktualizaci na kompilátor, který implementuje tuto funkci jazyka, je možné pozorovat následující zásadní změny.

  • Pokud jsou povolená aktualizovaná pravidla zabezpečení paměti (což může být výchozí nebo dokonce jediná možnost v budoucí .NET verzi):
    • unsafe člen nyní označí také jako nebezpečné, což znamená, že volající musí být v unsafe kontextu a přepsání nemůže být unsafe , pokud je základní člen bezpečný.
    • unsafe u členu nebo typu nezavádí unsafe automaticky kontext, což znamená, že explicitní unsafe bloky musí být použity v unsafe operacích v členských orgánech a inicializátorech.
    • extern členové a pole v explicitním rozložení vyžadují explicitníunsafe/safe klíčové slovo pro deklaraci.
    • stackalloc za určitých podmínek vyžaduje unsafe kontext.
    • unsafe modifikátor je chyba u deklarací typů, statických konstruktorů a destruktorů, protože nemá žádný vliv.
  • V rámci nové langversion:
    • Lambda odvozování může zvážit více kandidátů, což vede k nejednoznačnostem rozlišení přetížení.
    • safe je teď kontextové klíčové slovo, které by mohlo narušit kód, který ho použil jako typ.
    • Režim compat v režimu starší verze může vést k dalším chybám.

Podrobný návrh

Terminologie: Označujeme členy jako nebezpečné (dříve označované jako nebezpečné volající), pokud

Syntax

Tento návrh zavádí:

Tato nová syntaxe je dostupná v rámci nového jazyka LangVersion, ale bez ohledu na výslovný souhlas v místním prostředí, které se snažíme nastavit tak, aby se vše, co je potřeba udělat, když jste přihlášeni, můžete před přihlášením provést.

Existující unsafe pravidla

Stávající specifikace jazyka C# má velkou část věnovanou unsafe: §24 Nebezpečný kód. Definuje se jako podmíněně normativní, protože pro podporu této funkce se nevyžaduje platný kompilátor jazyka unsafe C#. Většina toho, co se v současné době považuje za podmíněnou normativní, už nebude tak po této změně, protože většina definic ukazatelů se už nepovažuje za nebezpečné. Typy ukazatelů, pevné a pohyblivé proměnné, všechny výrazy ukazatele (s výjimkou nepřímých ukazatelů, přístup ke členům ukazatele a přístup k prvkům ukazatele) a fixed příkaz se už unsafenepovažují za normální a neexistují v normálním jazyce C# bez nutnosti použití v unsafe kontextu. Podobně deklarování vyrovnávací paměti s pevnou velikostí nebo inicializované inicializace stackalloc jsou také dokonale legální v bezpečném jazyce C#. Pro všechny tyto případy je přístup pouze k paměti, která je nebezpečná.

Důležité je, že tyto odpočinek ukazatele platí bez ohledu na to, zda se sestavení přihlásí k aktualizovaným pravidlům bezpečnosti paměti. Pouze operace, které skutečně dereference nebo jinak přímo přistupují k paměti, nadále vyžadují unsafe kontext. To umožňuje postup přírůstkové migrace: uživatelé můžou změnit tvar existujícího kódu tak, že se unsafe přesunou dovnitř, když člen zpracuje riziko interně nebo směrem ven, když se volající musí zúčastnit auditu, a teprve potom překlopí přepínač výslovného souhlasu pro celé sestavení.

Vzhledem k rozsáhlému přepisu oddílu unsafe kódu i dalších částí specifikace jazyka C#, které jsou součástí této změny, by bylo nepraktné a pravděpodobně by nebylo užitečné poskytnout řádkový rozdíl stávajících pravidel specifikace. Místo toho poskytneme přehled změn, které se mají provést v dané části, a také konkrétní nová pravidla pro to, co je povoleno v unsafe kontextech.

Opětovné definice výrazů, které vyžadují nebezpečné kontexty

Následující výrazy vyžadují unsafe kontext při použití:

Kromě těchto výrazů mohou výrazy a příkazy také podmíněně vyžadovat unsafe kontext, pokud závisí na libovolném symbolu, který je označen jako unsafe. Například volání metody, která vyžaduje-nebezpečné , způsobí , že invocation_expression vyžadovat unsafe kontext. Příkazy s vloženými vyvoláním (například usings, foreacha podobnými) můžou také vyžadovat unsafe kontext, když používají člena vyžadujícího nebezpečného uživatele.

Když v tomto dokumentu řekneme "vyžaduje nebezpečný kontext" nebo podobný, znamená to vygenerování chyby, že konstruktor vyžaduje unsafe použití kontextu.

Note

Tato část pravděpodobně potřebuje formálně deklarovat, co každý výraz a příkaz musí zvážit, aby vyžadoval kontext unsafe .

Typy ukazatelů

Jak už bylo zmíněno, ukazatele už nejsou ze své podstaty nebezpečné. Veškeré odkazy na nebezpečné kontexty v §24.3 jsou odstraněny. Typy ukazatelů existují v normálním jazyce C# a nevyžadují unsafe jejich uvedení do existence. Definice typů by měly být převedeny do §8.1 a jeho následujících částí jako jiné typy.

Podobně by převody ukazatelů měly být převedeny na §10 s odkazy na unsafe kontexty odstraněny.

Podobně by výrazy ukazatelů s výjimkou nepřímých ukazatelů, přístupu ke členům ukazatele a přístupu k prvkům ukazatele měly být převedeny na §12 s odkazy na unsafe odebrané kontexty. Význam těchto výrazů se nemění; jedinou změnou je, že už nevyžadují unsafe použití kontextu.

U nepřímých ukazatelů, přístupu ke členům ukazatele a přístupu k prvkům ukazatele zůstávají tyto operátory nebezpečné, protože tyto přístupové paměti, které nejsou spravovány modulem runtime. Zůstanou v §24 a budou nadále vyžadovat unsafe použití kontextu. Jakékoli použití mimo unsafe kontext je chyba. Žádné sémantiky o těchto operátorech se nemění; stále stále znamenají přesně to samé, co dnes dělají. Tyto výrazy musí vždy nastat v unsafe kontextu.

Pevný příkaz se přesune na §13 s odkazy na unsafe kontexty odebrané.

Ukazatele funkcí ještě nejsou začleněny do hlavní specifikace jazyka C#, ale jsou podobně ovlivněny; vše kromě vyvolání ukazatele funkce se přesune do standardní specifikace. Výraz vyvolání ukazatele funkce musí vždy probíhat v unsafe kontextu.

Vyrovnávací paměti s pevnou velikostí

Text vyrovnávacích pamětí s pevnou velikostí je podobný ukazatelům. Definice vyrovnávací paměti s pevnou velikostí není sama o sobě nebezpečná a přejde na §16.3. Přístup k vyrovnávací paměti s pevnou velikostí ve výrazu je podobně bezpečný, pokud k výrazu nedojde jako primary_expression . element_accessTy se vyhodnocují jako pointer_element_access, což je nebezpečné podle výše uvedených pravidel.

Přidělení zásobníku

Scénář přidělování zásobníku je opět velmi podobný ukazatelům. Převod stackalloc ukazatele na ukazatel už není nebezpečný. Jedná se o odvozování tohoto ukazatele, který je nebezpečný. Přidáme ale jedno nové pravidlo:

Stackalloc_expression je nebezpečné, pokud jsou splněny všechny následující příkazy:

  • Stackalloc_expression se převádí na nebo Span<T> na ReadOnlySpan<T>.
  • Stackalloc_expression nemá stackalloc_initializer.
  • Stackalloc_expression se používá v rámci člena, který se SkipLocalsInitAttribute použil.

V těchto kontextech by výsledný prostor zásobníku mohl mít neznámý obsah paměti a je převeden na typ, který poskytuje bezpečný obálku kolem nespravovaného přístupu k paměti. To porušuje smlouvu Span<T> a ReadOnlySpan<T>a proto musí být předmětem dodatečné kontroly autora a kontrolorů takového kódu.

Na rozdíl od jiných změn unsafe pravidel, které jsou uvolněním, je to zpřísnění, a proto platí pouze v rámci výslovného souhlasu s aktualizovanými pravidly bezpečnosti paměti, aby se zabránilo přerušení.

Note

To znamená, že přiřazení stackalloc ukazatele je vždy bezpečné bez ohledu na kontext.

Všimněte si, že stackalloc spravovaný typ zůstává chybou.

sizeof

U některých předdefinovaných typů sizeof byly vždy konstantní a bezpečné (§12.8.19) a které zůstávají beze změny. U jiných typů se sizeof používá k vyžadování nebezpečného kontextu (§24.6.9), ale je nyní bezpečné bez ohledu na souhlas s aktualizovanými pravidly bezpečnosti paměti.

Přepsání, dědičnost a implementace

Jedná se o chybu bezpečnosti paměti, která se má přidat unsafe na úrovni člena v jakémkoli přepsání nebo implementaci člena, který není původně nebezpečný , protože volající mohou používat základní definici a neuvidí žádné přidání unsafe odvozené implementace.

Delegáti a Lambda výrazy

Jedná se o chybu zabezpečení paměti pro převod člena, který vyžaduje nebezpečného člena unsafe na typ delegáta mimo kontext. Typy delegátů a typy funkcí nemohou být nebezpečné. Jedná se o chybu v době kompilace, která se použije unsafe u symbolu lambda.

extern

Vzhledem k tomu extern , že metody jsou v nativních umístěních, které nelze zaručit modulem runtime, kompilátor nemůže zjistit, jestli jsou bezpečné nebo nebezpečné. Dokonce i metody, které berou unmanaged parametry podle hodnoty, nelze bezpečně volat jazykem C#, protože volání používané pro metodu může být nesprávně zadáno uživatelem a musí být ručně ověřeno kontrolou.

Proto kompilátor v rámci aktualizovaných pravidel bezpečnosti paměti vyžaduje, aby každá extern metoda byla explicitně označena jako buď unsafe nebo safe.

extern metody ze sestavení používajících starší pravidla bezpečnosti paměti nejsou považovány za implicitně unsafe , protože extern se považuje za podrobnosti implementace, které nejsou součástí veřejného povrchu. extern není zaručeno zachování v referenčních sestaveních.

Všimněte si, že se liší od režimu kompatibility , který platí i pro sestavení starších pravidel, protože metody s ukazateli v podpisu by vždy potřebovaly nebezpečný kontext v lokalitě volání.

Nebezpečné modifikátory a kontexty

Jak je popsáno v nezabezpečené specifikaci kontextu, unsafe chová se lexikálním způsobem a označuje celé textové tělo obsažené unsafe blokem unsafe jako kontext (s výjimkou těla iterátoru) a také některé okolní kontexty v případě deklarací:

class A : Attribute
{
    public A(object o) { }
}
class C
{
    [A(default(int*[]))] void M1() { } // error: using pointers outside `unsafe` context
    [A(default(int*[]))] unsafe void M2() { } // ok
}

Pokud se přihlásíte k aktualizovaným pravidlům bezpečnosti paměti, unsafe označí ho člen jako nebezpečný, rozšíří povinnost auditu volajícímu a nezavedáunsafe kontext (místo toho pouze explicitní unsafe oblasti v těle vytvářejí unsafe kontexty).

unsafe v následujících deklaracích vznikne chyba, protože již nemá význam:

  • delegate,
  • statický konstruktor,
  • Destruktor
  • deklarace typu (classatd struct.).

unsafe v konstruktoru unsafe zavádí kontext uvnitř inicializátoru, tj unsafe . konstruktor může volat vyžaduje-nebezpečnébase nebo this konstruktor.

Typy bez parametrů vyžadují nebezpečné konstruktory, které nevyhovují new() omezení. Podobně a kromě toho struktury bez parametrů nevyžadují nebezpečné konstruktory struct omezení.

unsafe u člena se nepoužije na žádné vnořené anonymní nebo místní funkce uvnitř člena. Totéž platí pro anonymní a místní funkce deklarované uvnitř unsafe bloku (jsou stále v unsafe kontextu jako vždy, ale nejsou nutné-nebezpečné). Pokud chcete označit místní funkci jako povinnou, je nutné ji ručně označit jako unsafe. Lambda nelze označit jako nebezpečné ( unsafe klíčové slovo je pro ně zakázáno).

Pokud je partialčlenem , obě části musí souhlasit s modifikátorem unsafe , beze změny od pravidel jazyka C#.

partial class C1
{
    public partial void M1(); // Error: both parts must be unsafe, or neither can be
    public partial unsafe void M2();
}

partial class C1
{
    public unsafe partial void M1() => Console.WriteLine("hello world");
    public partial void M2() => Console.WriteLine("hello world"); // Error: both parts must be unsafe, or neither can be
}

Pro vlastnosti a get přístupové objekty lze nezávisle deklarovat jako set/init; označení celé vlastnosti jako unsafe znamená, že jak objekty unsafeget, set/init tak přístupové objekty jsou nebezpečné. V současné době není možné u přístupových objektů událostí umisťovat žádné modifikátory a tento návrh nemění, že to znamená, add že remove přístupové objekty událostí nelze nezávisle deklarovat jako unsafe. Pouze pokud je celá událost označena jako unsafe, znamená to, že přístupové objekty jsou nebezpečné; jinak jsou bezpečné.

Fields

unsafe v poli je také označí jako nebezpečné a nezavádí unsafe kontext v inicializátoru. Režim compat platí také pro pole.

Označení vlastnosti nebo události jako unsafenezabezpečuje její záložní pole.

V typu s [StructLayout(LayoutKind.Explicit)] nebo [ExtendedLayout]musí být všechna pole instance označena buď safe nebo unsafe. Pokud je pole "skryté" za událostí typu automatické vlastnosti nebo pole, safe/unsafe požadavek se místo toho přesune do události typu auto-property nebo pole.

Metadata

Při kompilaci sestavení s novými pravidly bezpečnosti paměti se označí MemorySafetyRulesAttribute (podrobně níže) jako 15 jazyková verze. To je signál pro všechny podřízené příjemce, že všechny členy definované v sestavení budou správně přiřazeny RequiresUnsafeAttribute (podrobně níže), pokud unsafe je potřeba volat kontext. Každý člen v takovém sestavení, který není označený RequiresUnsafeAttribute , nevyžaduje unsafe , aby byl volán kontext bez ohledu na typy v podpisu člena.

Jedná se o chybu použití symbolu nebo MemorySafetyRulesAttribute jakéhokoli symbolu RequiresUnsafeAttribute explicitně ve zdroji.

Kompilátor ignoruje RequiresUnsafeAttributečleny označené -označenými ze sestavení, která používají starší pravidla zabezpečení paměti (místo toho se používá režim kompatibility ).

Pokud je člen bez typu označen jako unsafe, kompilátor syntetizuje RequiresUnsafeAttribute aplikaci na členu v metadatech. Když uživatel vyžaduje nebezpečného člena, který generuje skryté členy, jako jsou metody get/set automatické vlastnosti, jsou členy přístupné uživateli i všechny skryté členy vygenerované tímto uživatelem jsou všechny nebezpečné a RequiresUnsafeAttribute jsou použity pro všechny.

MemorySafetyRulesAttribute A RequiresUnsafeAttribute definice je syntetizována kompilátorem v případě potřeby na standardní dobře známá členová pravidla.

namespace System.Runtime.CompilerServices
{
    /// <summary>Indicates the language version of the memory safety rules used when the module was compiled.</summary>
    [AttributeUsage(AttributeTargets.Module, Inherited = false)]
    public sealed class MemorySafetyRulesAttribute : Attribute
    {
        /// <summary>Initializes a new instance of the <see cref="MemorySafetyRulesAttribute"/> class.</summary>
        /// <param name="version">The language version of the memory safety rules used when the module was compiled.</param>
        public MemorySafetyRulesAttribute(int version) => Version = version;
 
        /// <summary>Gets the language version of the memory safety rules used when the module was compiled.</summary>
        public int Version { get; }
    }

    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
    public sealed class RequiresUnsafeAttribute : Attribute
    {
    }
}

Režim kompatibility

Pro účely kompatibility a snížení počtu falešně negativních výsledků, ke kterým dochází při povolování nových pravidel, máme záložní pravidlo pro moduly, které nebyly aktualizovány na nová pravidla. U takových modulů je člen považován za nebezpečný , pokud obsahuje ukazatel nebo typ ukazatele funkce někam mezi jeho typy parametrů nebo návratový typ (může být vnořený do jiného typu než ukazatele, např int*[]. ). Všimněte si, že to neplatí pro ukazatele v typech omezení (např where T : I<int*[]>. ) stejně jako ty, které by dříve nepotřebovaly nebezpečný kontext na webech volání.

Nezahrnuje nahrazené obecné parametry (například metodu I<T>.M(T) při nahrazování Tint*[]) protože neexistuje žádný typ-bezpečný způsob, jak cílový člen použít tento typ ukazatele pro cokoli stejně.

Takový režim kompatibility vyžaduje nebezpečné členy, které vyžadují unsafe použití kontextu i od volajících, kteří se nepřihlásili k aktualizovaným pravidlům zabezpečení paměti. To by se mělo vyhnout "poklesu", kdy pouze aktualizace jazyka LangVersion (ale ne aktualizace verze pravidel zabezpečení paměti) zajišťuje bezpečnost operací ukazatele (včetně volání funkcí s ukazateli v podpisu, které budou pravděpodobně označeny jako nezbytné-nebezpečné , když se přihlásíte k aktualizovaným pravidlům), a proto je kód méně chráněný v tomto okně migrace.

VB

Pro Visual Basic pro členy, které vyžadují nebezpečné členy, není potřeba přidávat podporu, protože VB dnes neexistují žádné unsafe kontexty a žádný způsob, jak tam pracovat s ukazateli.

unsafe Výrazy

Výraz unsafe představuje minimální unsafe kontext pro vyhodnocení jednoho výrazu. Je užitečné v situacích, kdy unsafe by blok zbytečně rozšiřoval unsafe obor nebo kde unsafe bloky nelze syntakticky použít (například v catch filtrech, inicializátorech polí, inicializátorech konstruktoru a pozici operandu await).

Syntax

Přidá unsafe_expression se jako primary_no_array_creation_expression:

unsafe_expression
    : 'unsafe' '(' expression ')'
    ;

Podléhá stejnému AllowUnsafeBlocks požadavku jako klíčové unsafe slovo jinde.

Sémantika

Vytvoří unsafe_expression kontext pro vyhodnocení unsafe. To znamená, že v uzavřeném výrazu jsou povoleny odkazy ukazatele, vyvolání ukazatele funkce a volání vyžadujících nebezpečné členy. Typ a hodnota unsafe_expression jsou typ a hodnota uzavřeného výrazu.

Kontext unsafe vytvořený objektem unsafe_expression nepřesahuje za pravou závorku.

Příklady motivace a migrace

Několik syntaktických pozic vůbec nepřipouští unsafe bloky, ale může obsahovat dílčí výrazy, které volání vyžaduje nebezpečné členy. Bez unsafe výrazů vyžaduje migrace takového kódu extrahování nebezpečného dílčího výrazu do pomocné místní funkce nebo dočasné proměnné, která zakrývá záměr a zvyšuje úroveň podrobností.

await v metodě vyžadující nebezpečné . Výraz await se nemůže objevit uvnitř unsafe bloku. Když se očekávaná metoda stane vyžadovat-nebezpečné, dočasná proměnná je nutná k uložení úkolu předtím, než bude možné očekávat:

// Without unsafe expressions: must spill to a temporary
Task t;
// SAFETY: Discharges obligations because reasons
unsafe { t = DoWork(); }
await t;

// With unsafe expressions: the unsafe context wraps only the call;
// the await remains outside it and is fully legal
// SAFETY: Discharges obligations because reasons
await unsafe(DoWork());

Zachyťte filtry. Filtr whencatch klauzule je výraz, nikoli text příkazu. unsafe Blok může ohraničit pouze příkazy, takže neexistuje místo, kde by se dal umístit jenom kolem výrazu filtru. Kromě toho, pokud try tělo obsahuje await výraz, unsafe blok nemůže ohraničí celý try/catchpříkaz –await není povolen uvnitř unsafe bloku. Když se metoda použitá ve filtru stane nebezpečnou, jedinou alternativou bez unsafe výrazů je pomocná rutina:

// Without unsafe expressions: must spill to a local function
static bool FilterHelper(Exception e)
{
    // SAFETY: Discharges obligations because reasons
    unsafe { return NowUnsafeCall(e); }
}

try
{
    await DoWork(); // 'await' here prevents wrapping the whole try/catch in 'unsafe'
}
catch (Exception e) when (NowUnsafeCall(e))
{
}

// With unsafe expressions: inline and minimal scope
try
{
    await DoWork();
}
// SAFETY: Discharges obligations because reasons
catch (Exception e) when (unsafe(NowUnsafeCall(e)))
{
}

Inicializátory polí V rámci aktualizovaných pravidel unsafe v poli nezavádí unsafe kontext v inicializátoru. Když inicializátor pole volá člena, který vyžaduje nebezpečného člena, unsafe výraz poskytuje kontext, aniž by vyžadoval pomocnou metodu:

// Without unsafe expressions: must spill to a helper method
static int InitialValue()
{
    // SAFETY: Discharges obligations because reasons
    unsafe { return ReadFromPointer(); }
}
static int _value = InitialValue();

// With unsafe expressions: inline
// SAFETY: Discharges obligations because reasons
static int _value = unsafe(ReadFromPointer());

Inicializátory konstruktoru this(...) seznamy base(...) argumentů inicializátoru jsou výrazy, nikoli těla příkazů. Pokud některý z těchto argumentů volá člena vyžadujícího nebezpečného , není místo pro vložení unsafe bloku, takže volání musí být jinak přesunuto do pomocné rutiny:

class C(int x)
{
    // SAFETY: Discharges obligations because reasons
    C() : this(unsafe(GetUnsafeValue()))
    {
    }
}

class Derived : Base
{
    // SAFETY: Discharges obligations because reasons
    Derived() : base(unsafe(GetUnsafeValue()))
    {
    }
}

Vložená volání. Obecně platí, že zabalení pouze podsítě vyžaduje nezabezpečené zachová obor auditu pevně a vyhne se vyžádání okolního bezpečného kódu (například vyhodnocení argumentu nebo volání metody obsahujícího) do unsafe kontextu:

extern int Add(int i1, int i2); // Some fancy extern addition function

// Code I want to write:

// SAFETY: Discharges obligations because reasons
Console.WriteLine(unsafe(Add(1, 2)));

// Code I have to write without unsafe expressions, option 1
// (unsafe context unnecessarily includes the WriteLine call):

// SAFETY: Discharges obligations because reasons
unsafe
{
    Console.WriteLine(Add(1, 2));
}

// Code I have to write without unsafe expressions, option 2
// (very verbose and harder to read):
int result;
// SAFETY: Discharges obligations because reasons
unsafe
{
    result = Add(1, 2);
}
Console.WriteLine(result);

Dotazy

(zodpovězeno) Slouží RequiresUnsafeAttribute k označení nebezpečných členů.

Místo použití unsafe klíčového slova na členu k označení vyžaduje nebezpečné členy bychom mohli použít atribut (RequiresUnsafeAttribute) použitý na člena (a ne změnit význam modifikátoru unsafe u členů).

unsafeVýhody:

  • podobně jako v jiných jazycích, a proto je srozumitelnější,
  • zjistitelnější než atribut.

Výhody atributu (nebo jiného klíčového slova):

  • zabraňuje přerušení stávajících členů označených jako unsafe,
  • možné přírůstkové přijetí (člen by-member),
  • nenutí označení celého těla jako unsafe (i s unsafe klíčovým slovem, které bychom mohli změnitunsafe , aby to nemělo vliv na těla),
  • umožňuje potlačit všechny chyby vyžadující nebezpečné chyby, aniž by bylo nutné označit člen samotný jako nebezpečný (příklady).

Diskuse:

Odpověď: Použití klíčového slova unsafe k označení vyžaduje nebezpečné členy.

Místní funkce / bezpečné kontexty lambda

Právě teď unsafe je tělo metody lexicky vymezeno. Všechny vnořené místní funkce nebo lambda to dědí a jejich těla jsou v nezabezpečeném kontextu paměti. Je toto chování, které chceme zachovat v jazyce? Upozorňujeme, že pokud ponecháme unsafe jako modifikátor použitý ke zveřejnění, že volající musí být nebezpečný, může to mít vliv na podpis metody. Jak je v současné době navrhováno, vnořené anonymní a místní funkce neudržují nebezpečný kontext jejich člena.

Typ unsafedelegáta ty

Mohli bychom povolit označení typů delegátů a lambda (a typů funkcí) jako nebezpečné. To by vyžadovalo několik dalších pravidel (mimo unsafe kontext):

  • zakázat používání delegátů typu vyžaduje nebezpečné delegáty,
  • zakázat převod těchto delegátů na cokoli, co nevyžaduje-nebezpečné (Delegate,Expression, a object), Bez toho existuje riziko vynucení unsafe poznámek na nesprávném místě a mít oblast, kde skutečná oblast unsafety není správně označena.

Převod skupiny lambda/metod na bezpečné typy delegátů

Pokud povolíme unsafe lambda a delegáty, měli bychom převod skupiny metod lambda nebo vyžadování na typ delegáta, který není vyžadován , povolený bez upozornění nebo chyby v unsafe kontextu? Pokud to neuděláme, mohlo by to být pro různé části ekosystému poměrně bolestné, zejména všechny výčty, které se předávají prostřednictvím dotazů LINQ.

Přirozené typy lambda/metody

V současné chvíli se jediný skutečný dopad na sémantiku a codegen (kromě dalších metadat) mění function_type lambda nebo skupiny metod, pokud unsafe je v podpisu. Pokud bychom se tomu vyhnuli, pak by to nemělo žádný skutečný dopad, což by mohlo dát uživatelům větší jistotu, že chování se pod kapotou nezměnilo.

Note

Pokud se rozhodneme zachovat možnost unsafe mít lambda, musíme tento návrh aktualizovat tak, aby zahrnoval změnu syntaxe, aby se lambda mohla deklarovat unsafe na prvním místě.

Ukazatele na spravované typy

C# 11 umožňuje ukazatele na spravované typy s upozorněním. Měli bychom toto upozornění uvolnit pro řešení operací? Myslíme si, že problém je pouze tehdy, když uživatel překáže takový ukazatel, který spadá pod normální nebezpečná pravidla vývoje. Ale co?sizeof

stackalloc inicializováno

Specifikace dnes vždy považuje stackalloc paměť za neinicializovanou a říká, že obsah není definován, pokud ručně nezaškrtnuté nebo přiřazené. Považujeme tuto chybu specifikace nebo potřebujeme změnit to, co považujeme unsafe za stackalloc účelem?

stackalloc pravidlo

Nástroj LDM by měl potvrdit stackalloc výše definované pravidlo a zjistit, jestli se má použít bez ohledu na výslovný souhlas, jako jsou jiné změny související s ukazateli.

AllowUnsafeBlocks

AllowUnsafeBlocks Význam je v současné době beze změny – je nutné ho nastavit, true aby bylo možné použít unsafe klíčové slovo nebo SkipLocalsInitAttribute. Neměli bychom ho vyžadovat v SkipLocalsInitAttribute rámci aktualizovaných pravidel, protože seznam BCL může tento atribut označit jako nebezpečný? Měli bychom ho safe také vyžadovat pro klíčové slovo? Měli bychom ji vyžadovat pro unsafe bloky i unsafe deklarace členů nebo pro jinou kombinaci těchto deklarací?

(zodpovězeno) unsafe Výrazy

Další jazyky s komplexnějšími unsafe funkcemi byly přidány unsafe jako výraz, který zlepšuje uživatelskou ergonomii a umožňuje autorům přesněji omezit, kde unsafe se používá. Je to něco, co chceme mít v jazyce C#? Zvažte vložené volání člena unsafe , který zpracovává bezpečnost přímo: v tuto chvíli by autor buď potřeboval zabalit celý příkaz do unsafe bloku, rozšířit rozsah unsafe kontextu, nebo by potřeboval rozdělit vnitřní volání funkce do přechodné proměnné.

extern int Add(int i1, int i2); // Some fancy extern addition function

// Code I want to write:
Console.WriteLine(unsafe(Add(1, 2)));

// Code I have to write option 1, unsafe context unnecessary includes the WriteLine call
unsafe
{
    Console.WriteLine(Add(1, 2));
}

// Code I have to write option 2, very verbose and harder to read:
int result;
unsafe
{
    result = Add(1, 2);
}
Console.WriteLine(result);

Odpověď: Ano. Podívejte se na podrobný oddíl návrhu.

Další unsafe kontexty a odpočinek

Měli bychom uvolnit další omezení týkající se unsafe parametrů iterátorů a asynchronních metod a ukazatelů? Zvláště povolení await UnsafeMethod() by bylo užitečné, protože teď uživatelé musí přepsat to na Task t; unsafe { t = UnsafeMethod(); } await t;. Další podrobnosti najdete v tématu ref/unsafe in iterators/async .

Měli bychom také povolit &UnsafeMethod bezpečný kontext? V současné podobě návrhu to vyžaduje unsafe kontext, pokud je metoda označena jako unsafe. Ale vzhledem k tomu, že právě získáváme jeho adresu, která bude potřebovat unsafe kontext při dereferenced/zavolání, můžeme povolit adresu sama v bezpečném kontextu.

Nebezpečné odpočinek v bráně na LangVersion

Měli bychom učinit nebezpečné kontext uvolnění nepodmíněné na LangVersion?

  • LDM 2026-04-06: nejsou podmíněné na verzi pravidel zabezpečení paměti
  • Todo: co LangVersion?

(zodpovězeno) unsafe na typech

Nemohli bychom zvážit, aby celý lexikální obor unsafe typu byl unsafe kontextem a upozorňoval unsafe na typ, protože by neměl žádný význam.

  • LDM 2025-11-12: unsafe u typu nebude mít žádný význam
  • LDM 2026-05-13: bude to chyba (můžete se znovu vrátit na základě zpětné vazby)

Odpověď: unsafe U typu je chyba (můžete se vrátit na základě zpětné vazby) v aktualizovaných pravidlech.

unsafe na přístupových zařízeních

Nově povolujeme unsafe přístupové objekty vlastností, ale ne u přístupových objektů událostí, a to v souladu s jinými existujícími modifikátory. To také znamená, že unsafe u vlastnosti je jen zkratka pro unsafe jeho přístupové objekty. Současně ale vyžadujípartial, unsafeaby modifikátory odpovídaly:

partial class C
{
    unsafe partial int P { get; set; } // effectively both `get` and `set` are `unsafe` here
    unsafe partial int P { unsafe get => 0; set { } } // still an error: `unsafe` on `get` doesn't match
}

// similar to this pre-existing behavior:
unsafe partial class D
{
    unsafe partial void M();
}
unsafe partial class D
{
    partial void M() { } // error about missing `unsafe`
}

Možná by se měl chovat podobně jako readonly, tj. zakázat unsafe jak vlastnost, tak jeho příslušenství ve stejnou dobu:

partial struct S
{
    readonly partial int P { get; set; }

    // error: Both partial member declarations must be readonly or neither may be readonly
    partial int P { readonly get => 0; set { } }

    // error: Cannot specify 'readonly' modifiers on both property or indexer 'S.P2' and its accessor. Remove one of them.
    readonly int P2 { readonly get => 0; set { } }
}

(zodpovězeno) Povolit potlačení chyb vyžadujících nebezpečné chyby v hraničních scénářích

Jak bychom měli povolit potlačení chyb vyžadujících nebezpečné chyby v následujících scénářích?

class A : System.Attribute
{
    unsafe public A() { } // declaring requires-unsafe constructor
}

class C
{
    [A] public void M() { } // error: applying requires-unsafe `A..ctor`
}

class B : A
{
    public B() { } // error: calling requires-unsafe `A..ctor` (implicit `: base()`)
}

class X<T> where T : new();
class D
{
    public void M(X<A> x) { } // error: using `X` which uses requires-unsafe `A..ctor`
}

Abychom mohli potlačit chyby vyžadující nebezpečné , musíme v podpisu těchto členů nějak zavést unsafe kontext. unsafe Klíčové slovo unsafe nezavádí kontext. V podpisu bychom mohli zavést unsafeunsafe kontext, ale vynucení vytvoření konstruktoru vyžaduje nebezpečné , když chceme volat konstruktor vyžadující základ– nebezpečný konstruktor, zdá se nešťastný. V upozorněních na podpisy bychom mohli vyžadovat nebezpečné použití, které by uživatelé mohli potlačit na místě, pokud by narazili na tyto vzácné hraniční případy.

Existuje podobný problém s typy, protože unsafe v nich není žádný unsafe kontext:

class A : Attribute
{
    unsafe public A() { } // declaring requires-unsafe constructor
}

[A] class C; // error: applying requires-unsafe `A..ctor`

class B() : A(); // error: calling requires-unsafe `A..ctor`

class X<T> where T : new();
class D : X<A>; // error: inheriting from `X` which uses requires-unsafe `A..ctor`
  • LDM 2026-05-13:
    • nespustitelný kód, jako je aplikace atributů, by měl zůstat neuskutečitelným chybou (dokud neuslyšíme zpětnou vazbu).
    • jiné hraniční případy, jako new() je, mohou také zůstat chybou, dokud neuslyšíme zpětnou vazbu.
    • Nepovolujte deklarování bezparametrových konstruktorů, které nejsou nebezpečné
    • unsafe Pouze inicializátor konstruktoru může volat nezabezpečenýbase konstruktor nebo this konstruktor

kolekce params

class C
{
    unsafe public C() { } // declaring requires-unsafe constructor
}

class B
{
    public void M(params C c) { }
}

Tato deklarace může být jednoduše povolena, protože volání takové metody vyžaduje unsafe kontext, protože nebezpečná params kolekce je vytvořena na webu volání. I když deklarace skutečně vyžaduje nebezpečné, mohli bychom jen vyžadovat poznámku unsafe nebo alespoň upozornit na tuto skutečnost. Všimněte si, že params případ kolekce je dnes chybou v souladu s tím, jak se zde chovají další podobné funkce (Obsolete, UnmanagedCallersOnly), ale to může být chyba implementace.

Známí členové

Pro jednoduchost a sanitu implementace navrhujeme, aby kompilátor mohl předpokládat, že všichni dobře známí členové (například Array.Length) mohou být považováni za bezpečné (tj. nevyžaduje-nebezpečné).

Dokumentace XML

Předání povinnosti volajícím nese odpovědnost za to, aby bylo jasné, co je tato povinnost. Měli bychom to formalizovat nad rámec toho, co už je možné reprezentovat v dokumentech XML?

Členové, kteří mají být označeni unsafe , by měli mít komentáře, které volající musí udělat, aby se zajistilo, že je kód správný. Aby bylo možné snadněji rozpoznat a usnadnit rozlišení v dokumentaci, byla by užitečná nová značka dokumentu XML: <safety />. Bylo by očekáváno, že všechny předběžné a následné podmínky budou umístěny v <safety> bloku.

Chcete-li zdokumentovat odůvodnění každého unsafe bloku, doporučujeme použít // SAFETY komentáře, podobně jako Rust. Měli bychom je také zkontrolovat kompilátorem (např. máte nějaké upozornění mimo výchozí nastavení) nebo to nechat na analyzátoru?

Další bezvýznamná unsafe upozornění

Měla by další deklarace způsobit bezvýznamné unsafe upozornění nebo chybu? Například metody s prázdnými těly (nebo extern) atd. Analyzátor INTEGROVANÉho vývojového prostředí (IDE) už ale máme nepotřebné unsafe .

Měla by [ModuleInitializer] unsafe void M() { } se jednat o chybu, podobně jako u statického konstruktoru?

(zodpovězeno) unsafe Pole

Dnes se v terénu neprobíná unsafe žádný návrh. Možná ho ale budeme muset přidat, aby jakékoli čtení z nebo zápisu do pole označeného unsafe jako unsafe mělo být v kontextu. To by nám umožnilo lépe anotovat obavy týkající se kódu, například:

class SafeWrapper
{
    internal byte* _p;

    public void DoStuff()
    {
        unsafe
        {
            // ... validate that the object state is good ...
            // ... perform operation with _p .... 
        }
    }
}

// Elsewhere in safe code:
void M(SafeWrapper w)
{
     w._p = stackalloc byte[10];
}

Měli bychom také označit záložní pole automatické vlastnosti jako unsafe?

Abychom mohli být v souladu s naším rozhodnutím pro členy, bylo by vhodné, unsafe aby se v terénu také nezavedly unsafe kontext. Pokud se v inicializátoru pole používají nebezpečné operace, může je uživatel vždy zapouzdřit do metody nebo bychom mohli zavést unsafe výrazy.

  • LDM 2026-05-13: pole mohou být označena jako nebezpečná prostřednictvím unsafe; inicializátory nejsou v unsafe kontextu.

(zodpovězeno) Explicitní rozložení

Mají být pole ve strukturách označená jako [StructLayout(Explicit)] nebo [ExtendedLayout] musí být označena jako unsafe?

Doporučení: Ano.

  • LDM 2026-05-13: Ano, vyžaduje buď unsafe nebo safe, stejně jako pro externs.

Explicitní rozložení a backingová pole

Pokud kompilátor syntetizuje záložní pole pro událost typu typu auto-vlastnost nebo pole Explicit/Extended , měli bychom místo toho vyžadovat safe/unsafe u vlastnosti nebo události? Jinak by uživatel musel tyto automatické deklarace rozšířit do ručního pole plus deklarace členů obálky. A co primární parametr konstruktoru, který získá záložní pole? Modifikátor safe i unsafe modifikátor je v současné době u deklarace parametru zakázán.

[Out] a [SkipLocalsInit]

Vzhledem k tomu, že [Out] například VB nezaručuje inicializaci parametrů v kombinaci s [SkipLocalsInit]voláním těchto parametrů unsafe v jazyce C#. Na druhou stranu to vypadá, že volaný problém, že není vydržovat svou [Out] smlouvu (podobně by mohlo být nebezpečné mnoha dalšími způsoby).

Pokud se rozhodneme, že by tyto případy měly být unsafe, můžeme vyloučit metody, o kterých víme, že jsme se přihlásili (v současné době jsou z jazyka C#, který zaručuje správné použití [Out] , ale pokud jiné jazyky implementují nová pravidla, měly by to zaručit i).

Převzetí adresy neinicializované proměnné

Dnes, převzetí adresy ne rozhodně přiřazené proměnné může považovat tuto proměnnou rozhodně přiřazenou a vystavit neinicializovaného člena. Máme několik možností, jak to vyřešit:

  1. Před povolením použití operátoru adresy pro ně je nutné, aby byly proměnné jednoznačně přiřazeny.
  2. Zajistěte, aby byla adresa neinicializované proměnné nebezpečná.

Příklady:

static void SkipInit<T>(out T value)  
{
    // value is considered definitely assigned after the address-of
    fixed (void* ptr = &value);
}
int i;
// i is considered definitely assigned after the address-of
_ = &i;
// Incrementing whatever was on the stack
i++;

Hodnota MemorySafetyRulesAttribute

Jaká by měla být verze pravidel zabezpečení paměti s povolenou/aktualizovanou? 2? 15? 11? Přečtěte si také o nezabezpečené sadě SDK a postupném vyjádření souhlasu?

(zodpovězeno) Další postupné vyjádření souhlasu?

Když se dnes přihlásíte, získáte dvě věci najednou: vynucování nebezpečných pravidel ve vašem vlastním kódu a signál publikovaný pro uživatele (prostřednictvím atributu na úrovni sestavení), že vaše poznámky jsou záměrné. Mohou být uživatelé, kteří chtějí začít dostávat diagnostiku vynucení při přidávání poznámek, aniž by byli připraveni publikovat, že mají plně anotované sestavení. Měli bychom mít "střední" úroveň výslovného souhlasu, která by zobrazila nebezpečnou diagnostiku jako upozornění, a úplné vyjádření souhlasu by je podporovalo na chyby?

(zodpovězeno) Jemně odstupňovaný souhlas

Poskytují jemně odstupňovaný mechanismus opt-in založený na oblastech podobný tomu, který jsme vytvořili pro odkazové typy s možnou hodnotou null, kde uživatelé mohou použít direktivy k povolení funkce pro konkrétní oblasti zdrojového kódu? Viz také Odhlášení nebo odhlášení pro oblasti kódu.

(zodpovězeno) extern implicitně nebezpečné

Toto je momentálně jediné místo, kde RequiresUnsafeAttribute je syntetizován bez explicitního unsafe klíčového slova. Jsme v pořádku s tím odlehlém?

CoreLib také dnes zveřejňuje mnoho extern metod (FCalls) jako bezpečné. Zacházení s extern metodami jako implicitně nebezpečné bude vyžadovat zabalení implicitně nebezpečných extern metod bezpečným obálkou. Můžeme narazit na situace, kdy přidání obálky navíc je obtížné kvůli podrobnostem implementace modulu runtime.

  • LDM 2026-04-01: extern Členové by měli být explicitně označeni jako bezpečné nebo nebezpečné
  • LDM 2026-04-06: stejné rozhodnutí bylo znovu
  • LDM 2026-04-13: dočasné rozhodnutí o použití safe klíčového slova
  • LDM 2026-05-13: použití safe klíčového slova (stále otevřené k reisitingu)

Odpověď: extern Členové musí být označeni buď unsafe nebo safe (přidáme pro něj nové klíčové slovo, ale můžete se k němu vrátit před odesláním funkce na základě zpětné vazby).

(zodpovězeno) unsafe výchozí hodnoty kontextu ve členech

Nepodařilo se nám automaticky vytvořit celé tělo unsafe metody unsafe jako kontext. Rust to udělal v RFC 2585 s motivací, že pomáhá snížit rozsah unsafe bloků na místa, ve kterých unsafe se skutečně používá. Totéž bychom mohli udělat v jazyce C#, a to buď jako upozornění, nebo chyba, s podobnými motivacemi.

(zodpovězeno) new() Omezení

Chceme podporovat new()funkce vyžadující-nebezpečné (něco, co v kompilátoru aktuálně nepodporujeme pro jiné funkce, jako je Obsolete)?

M<C>(); // should be an error outside `unsafe` context since `M` calls the requires-unsafe `C..ctor`?

void M<T>() where T : new()
{
    _ = new T();
}

class C
{
    unsafe public C() { }
}
  • LDM 2026-05-13: Typy s konstruktory bez parametrů vyžadující nebezpečné parametry by neměly splňovat new() omezení

new() constraint a usings

Jak se má chovat v aliasech a statických použitích?

  • Pokud se jedná o chybu v using deklaraci, je možné ji potlačit pomocí klíčového unsafe slova, které tam již podporujeme, nebo
  • měla by se v místě použití normálně zobrazovat chyba, jako by se používala přímo bez aliasu nebo statického použití?

Note

V druhém případě bychom museli přidat upozornění "bezvýznamné unsafe" pro použití aliasů a statických použití.

class C
{
    unsafe public C() { }
}

class D<T> where T : new()
{
    public static void M() { _ = new T(); }
}
using X = D<C>;
using unsafe X = D<C>;

X.M();
using static D<C>;
using static unsafe D<C>;

M();

Všimněte si, že další omezení se dnes chovají jako bývalá možnost:

using X = D<C>; // error here

_ = new X(); // ok
_ = new D<C>(); // error here

class C
{
    public C(int x) { }
}

class D<T> where T : new();

Měl by být unsafevíce konstruktorů?

  • dynamic (pravděpodobně by se měl shodovat s tím, co se BCL rozhodne pro rozhraní API reflexe)

(zodpovězeno) Jak se chceme zkosit?

Text otázky

Prvním návrhem je maximálně zásadní přístup, především jako lakmusový test, jak agresivní chceme být. Navrhuje možnost odhlásit nebo odhlásit oddíly kódu, změnit význam unsafe metod, zakázat použití typů unsafe , používat chyby místo upozornění a obecně vynucovat migraci najednou, v době upgradu kompilátoru (a pak se může opakovaně opakovat jako aktualizace závislostí a přidávat unsafe členy, které se už používaly). Máme ale spoustu zkušeností s prováděním změn, jako je tato, abychom se mohli zaměřit na rozsah rozpisů a umožnit přírůstkové přijetí. Tyto možnosti jsou popsané níže.

Odhlášení nebo odhlášení pro oblasti kódu

Není to poprvé, kdy jazyk C# znovu nadefinoval "základní" případ neoznačeného kódu. Jazyk C# 8.0 zavedl funkci referenčního typu s možnou hodnotou null, která se dá mnoha způsoby považovat za podrobný plán pro unsafe tvarování funkce. Měl podobné cíle (zabránit chybám, které stojí miliardy dolarů tím, že předefinuje způsob interpretace výchozího jazyka C#) a podobnou obecnou sadu funkcí (přidejte nové informace k typům pro šíření stavů a vyhněte se chybám). Bylo to také velmi zásadní a potřebovala silnou sadu výslovných souhlasů a vyjádřit výslovný nesouhlas, aby bylo možné tuto funkci v průběhu času přijímat pomocí základů kódu. Tato funkce je kontextem typu odkazu s možnou hodnotou null. Jedná se o lexikální obor, který informuje kompilátor, pro danou oblast v kódu, jak interpretovat neoznačené odkazy na typ a jaké typy upozornění dát uživateli. Můžeme ho použít také jako model unsafe a přidat kontext bezpečnostních pravidel nebo podobný, aby bylo možné řídit, jestli se tato nová pravidla používají, nebo ne.

Jednou z výhod, kterou máme s novými unsafe funkcemi, je, že jsou mnohem méně rozšířené. I když v top knihovnách existuje slušný počet unsafe volání, odhadneme procento hlavních knihoven, které používají unsafe , je mnohem nižší než "každý jeden řádek kódu jazyka C#, který jste kdy napsali". Doufáme, že to znamená, že i když je možná potřeba nějaká možnost odhlášení, nepotřebujeme tak komplikovaný mechanismus, jako je nullable, s vyhrazenými přepínači preprocesoru a podobně.

Upozornění vs. chyby

Návrh v současné době uvádí, že požadavky na bezpečnost paměti se v současné době vynucují upozorněním, nikoli chybou. To vychází z našich zkušeností při práci s funkcí s možnou hodnotou null, kdy upozornění umožňovala základ kódu přírůstkově přijmout novou funkci a nemusí převádět velké části kódu najednou. Očekáváme, že pro nebezpečná upozornění bude potřeba podobný proces: mnoho základů kódu bude moci jednoduše zapnout nová pravidla globálně a pokračovat v životě. Očekáváme ale, že základy kódu, které nám nejvíce záleží na přijetí nových pravidel, budou mít velké množství kódu k anotaci, a chceme, aby se s funkcí mohli pohybovat vpřed, místo toho, aby viděli stěnu chyb a okamžitě se vzdali. Díky upozorněním na požadavky umožňujeme těmto základům kódu opravit upozornění typu file-by-file nebo method-by-method podle potřeby a zakázat upozornění všude jinde.

Konce podpisů metody

Právě teď navrhujeme, aby unsafe se jako klíčové slovo metody přesunulo z něčeho, co je lexicky vymezeno bez sémantického dopadu na něco, co má sémantický dopad, a není lexicky vymezen. Tuto chybu můžeme omezit zavedením nového klíčového slova pro případ, kdy volající metody nebo člen musí být v unsafe kontextu, callerunsafe například jako modifikátor.

Výchozí hodnoty pro generátory zdrojů

Pro použití s možnou hodnotou null vynutíme, aby autoři generátoru explicitně přihlásili k hodnotě null bez ohledu na to, jestli se celý projekt ve výchozím nastavení přihlásil k funkci, aby se výstup generátoru nepřerušil tím, že uživatel zapne hodnotu null a zobrazí upozornění jako chybu. Měli bychom to samé udělat pro generátory zdrojů?

Conclusion

Zodpovězeno v LDM 2025-11-05. Chyby týkající se problémů s bezpečností paměti budeme hlásit, když jsou nová pravidla zapnutá a nebudou provedeny žádné výjimky pro generátory zdrojů.

(zodpovězeno) Chyby nebo upozornění?

(zodpovězeno) Dostupnost generátoru zdrojů

(zodpovězeno) Vyžadovat safe tvůrce pro členy s unsafe bloky nebo ukazateli?

(zodpovězeno) Režim kompatibility pro nevolené volající taky?

Odpověď: Nepovolené členy s ukazateli v podpisu jsou považovány za nebezpečné pro přihlášené volající.

(zodpovězeno) Chcete rozšířit režim kompatibility?

Měli bychom zvážit nint i System.IntPtr ukazatele? Měli bychom zvážit extern/DllImport , že volající, kteří nejsou přihlášeni, jako je potřeba, je také nebezpečný ? Měli bychom mít deka upozornění, když opted-in sestavení odkazuje na neovolené sestavení?

  • LDM 2026-04-29: žádná rozšíření (analyzátory by mohly pokrýt některé méně nebezpečné signály)