"mönsterbaserad användning" och "använda deklarationer"
Anteckning
Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.
Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader fångas upp i de relevanta LDM-anteckningarna (Language Design Meeting).
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-problem: https://github.com/dotnet/csharplang/issues/114
Sammanfattning
Språket lägger till två nya funktioner runt using
-instruktionen för att göra resurshanteringen enklare: using
bör känna igen ett disponibelt mönster utöver IDisposable
och lägga till en using
-deklaration på språket.
Motivation
using
-instruktionen är ett effektivt verktyg för resurshantering idag, men det kräver en hel del ceremonier. Metoder som har flera resurser att hantera kan bli syntaktiskt överväldigade med en serie using
-satser. Den här syntaxbördan räcker för att de flesta riktlinjer för kodningsformat uttryckligen har ett undantag kring klammerparenteser för det här scenariot.
Den using
deklarationen tar bort mycket av ceremonin här och får C# i nivå med andra språk som innehåller resurshanteringsblock. Med den mönsterbaserade using
kan utvecklare dessutom utöka den uppsättning typer som kan delta här. I många fall tas behovet bort att skapa wrapper-typer som bara finns för att låta värden användas i en using
-instruktion.
Tillsammans gör dessa funktioner det möjligt för utvecklare att förenkla och utöka scenarier där using
kan användas.
Detaljerad design
använda deklaration
Språket gör att using
kan läggas till i en lokal variabeldeklaration. En sådan deklaration har samma effekt som att deklarera variabeln i en using
-instruktion på samma plats.
if (...)
{
using FileStream f = new FileStream(@"C:\source\using.md");
// statements
}
// Equivalent to
if (...)
{
using (FileStream f = new FileStream(@"C:\source\using.md"))
{
// statements
}
}
Livslängden för en using
lokal utökas tills slutet av det område som den deklareras i. De lokala using
-variablerna kommer sedan att avvecklas i motsatt ordning mot den ordning de deklarerades.
{
using var f1 = new FileStream("...");
using var f2 = new FileStream("...");
using var f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
}
Det finns inga begränsningar kring goto
, eller någon annan kontrollflödeskonstruktion inför en using
-deklaration. I stället fungerar koden precis som för motsvarande using
-instruktion:
{
using var f1 = new FileStream("...");
target:
using var f2 = new FileStream("...");
if (someCondition)
{
// Causes f2 to be disposed but has no effect on f1
goto target;
}
}
En lokal som deklareras i en using
lokal deklaration är implicit skrivskyddad. Detta matchar beteendet för lokala variabler som deklareras i en using
-sats.
Grammatiken för språket för using
-deklarationer kommer att vara följande:
local-using-declaration:
'using' type using-declarators
using-declarators:
using-declarator
using-declarators , using-declarator
using-declarator:
identifier = expression
Begränsningar kring using
deklaration:
- Får inte visas direkt i en
case
etikett utan måste i stället finnas inom ett block inuti etikettencase
. - Får inte visas som en del av en
out
variabeldeklaration. - Måste ha en initialiserare för varje deklarator.
- Den lokala typen måste implicit konverteras till
IDisposable
eller uppfyllausing
mönster.
mönsterbaserad användning
Språket lägger till begreppet disponibelt mönster för ref struct
typer: det är en ref struct
som har en tillgänglig Dispose
instansmetod. Typer som passar engångsmönstret kan delta i en using
-instruktion eller deklaration utan att behöva implementera IDisposable
.
ref struct Resource
{
public void Dispose() { ... }
}
using (var r = new Resource())
{
// statements
}
Detta gör det möjligt för utvecklare att utnyttja using
för ref struct
typer. Dessa typer kan inte implementera gränssnitt i C# 8 och kan därför inte delta i using
-instruktioner.
Samma begränsningar från en traditionell using
-instruktion gäller även här: lokala variabler som deklareras i using
är skrivskyddade, ett null
värde kommer inte att orsaka att ett undantag kastas, osv. Kodgenereringen kommer endast att vara annorlunda eftersom det inte kommer att finnas någon typkonvertering till IDisposable
innan Dispose anropas.
{
Resource r = new Resource();
try {
// statements
}
finally {
if (r != null) r.Dispose();
}
}
För att passa engångsmönstret måste metoden Dispose
vara en tillgänglig instansmedlem, parameterlös och ha en void
returtyp. Det kan inte vara en tilläggsmetod.
Överväganden
Inget av dessa överväganden har implementerats i C# 8
skiftlägesetiketter utan block
En using declaration
är olaglig direkt i en case
etikett på grund av komplikationer kring dess faktiska livslängd. En möjlig lösning är att helt enkelt ge den samma livslängd som en out var
på samma plats. Det beslutades att den extra komplexiteten i funktionsimplementeringen och den enkla lösningen (lägg bara till ett block till case
-etiketten) inte motiverade att ta den här vägen.
Framtida expansioner
fasta lokala inställningar
En fixed
-instruktion har alla egenskaper hos using
-instruktioner som motiverade möjligheten att ha using
lokala variabler. Du bör även överväga att utöka den här funktionen till att även fixed
lokalbefolkningen. Reglerna för livslängd och ordning bör gälla lika bra för using
och fixed
här.
C# feature specifications