DisposeAsync metódus implementálása
A System.IAsyncDisposable felület a C# 8.0 részeként lett bevezetve. A metódust akkor IAsyncDisposable.DisposeAsync() implementálhatja, amikor erőforrás-tisztítást kell végrehajtania, ugyanúgy, mint az Elidegenítési metódus implementálásakor. Az egyik legfontosabb különbség azonban az, hogy ez az implementáció lehetővé teszi az aszinkron törlési műveleteket. Az DisposeAsync() aszinkron ártalmatlanítási műveletet képviselő aszinkron értéket ad vissza ValueTask .
A felület implementálásakor jellemző, hogy az IAsyncDisposable osztályok a felületet is implementálják IDisposable . A felület jó implementációs mintáját IAsyncDisposable szinkron vagy aszinkron ártalmatlanításra kell előkészíteni, azonban ez nem követelmény. Ha az osztály szinkron eldobása nem lehetséges, csak IAsyncDisposable elfogadható. Az ártalmatlanítási minta megvalósítására vonatkozó összes útmutató az aszinkron megvalósításra is vonatkozik. Ez a cikk feltételezi, hogy már ismeri az Dispose metódus implementálásának módját.
Figyelemfelhívás
Ha az interfészt IAsyncDisposable implementálja, de nem az IDisposable interfészt, az alkalmazás esetleg kiszivárogtathatja az erőforrásokat. Ha egy osztály implementálja IAsyncDisposable, de nem IDisposable, és egy fogyasztó csak hív Dispose
, akkor az implementáció soha nem hívna meg DisposeAsync
. Ez erőforrás-szivárgást eredményezne.
Tipp.
A függőséginjektálás tekintetében a szolgáltatások IServiceCollectionregisztrálásakor a szolgáltatás élettartama implicit módon történik az Ön nevében. Az IServiceProvider és a hozzá tartozó IHost vezénylési erőforrás-törlés. Pontosabban az implementációk IDisposableIAsyncDisposable és azok megfelelően vannak megsemmisítve a megadott élettartamuk végén.
További információ: Függőséginjektálás a .NET-ben.
Felfedezés DisposeAsync
és DisposeAsyncCore
módszerek
Az IAsyncDisposable interfész egyetlen paraméter nélküli metódust deklarál. DisposeAsync() Minden nem összevont osztálynak meg kell határoznia egy metódust DisposeAsyncCore()
, amely szintén visszaad egy ValueTask.
Olyan
public
IAsyncDisposable.DisposeAsync() implementáció, amely nem rendelkezik paraméterekkel.Olyan
protected virtual ValueTask DisposeAsyncCore()
módszer, amelynek aláírása:protected virtual ValueTask DisposeAsyncCore() { }
A DisposeAsync
metódus
A public
paraméter nélküli DisposeAsync()
metódust implicit módon hívjuk meg egy await using
utasításban, és a célja a nem felügyelt erőforrások felszabadítása, az általános törlés végrehajtása, valamint annak jelzése, hogy a véglegesítőnek, ha van ilyen, nem kell futtatnia. A felügyelt objektumhoz társított memória felszabadítása mindig a szemétgyűjtő tartománya. Emiatt szabványos implementációval rendelkezik:
public async ValueTask DisposeAsync()
{
// Perform async cleanup.
await DisposeAsyncCore();
// Dispose of unmanaged resources.
Dispose(false);
// Suppress finalization.
GC.SuppressFinalize(this);
}
Feljegyzés
Az aszinkron elidegenítési minta egyik elsődleges különbsége az elidegenítési mintához képest, hogy a túlterhelési metódusra DisposeAsync()Dispose(bool)
irányuló hívás argumentumként van megadva false
. A metódus megvalósításakor IDisposable.Dispose() azonban true
a rendszer át lesz adva. Ez segít biztosítani a szinkron megsemmisítési mintával való funkcionális egyenértékűséget, és biztosítja, hogy a véglegesítő kód elérési útjai továbbra is meghívásra kerülnek. Más szóval a metódus aszinkron módon fogja megsemmisíteni a DisposeAsyncCore()
felügyelt erőforrásokat, így nem szeretné szinkron módon megsemmisíteni őket. Ezért a hívás Dispose(false)
ahelyett, hogy Dispose(true)
.
A DisposeAsyncCore
metódus
A DisposeAsyncCore()
metódus a felügyelt erőforrások aszinkron törlésének vagy a felé irányuló kaszkádolt hívásoknak a végrehajtására DisposeAsync()
szolgál. Beágyazza a gyakori aszinkron tisztítási műveleteket, amikor egy alosztály örököl egy alaposztályt, amely a implementációja IAsyncDisposable. A DisposeAsyncCore()
módszer célja, hogy a virtual
származtatott osztályok egyéni törlést definiálhassanak a felülbírálásokban.
Tipp.
Ha implementációja IAsyncDisposable van sealed
, akkor nincs szükség a DisposeAsyncCore()
metódusra, és az aszinkron törlés közvetlenül a metódusban IAsyncDisposable.DisposeAsync() is elvégezhető.
Az aszinkron ártalmatlanítási minta implementálása
Minden nem összevont osztályt potenciális alaposztálynak kell tekinteni, mert örökölhetőek lehetnek. Ha implementálja az aszinkron ártalmatlanítási mintát bármely lehetséges alaposztályhoz, meg kell adnia a metódust protected virtual ValueTask DisposeAsyncCore()
. Az alábbi példák közül néhány a következőképpen definiált NoopAsyncDisposable
osztályt használ:
public sealed class NoopAsyncDisposable : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => ValueTask.CompletedTask;
}
Íme egy példa a típust használó aszinkron ártalmatlanítási minta implementálására NoopAsyncDisposable
. A típus a visszatéréssel implementál DisposeAsync
ValueTask.CompletedTask.
public class ExampleAsyncDisposable : IAsyncDisposable
{
private IAsyncDisposable? _example;
public ExampleAsyncDisposable() =>
_example = new NoopAsyncDisposable();
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
GC.SuppressFinalize(this);
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_example is not null)
{
await _example.DisposeAsync().ConfigureAwait(false);
}
_example = null;
}
}
Az előző példában:
- Ez
ExampleAsyncDisposable
egy nem összevont osztály, amely megvalósítja az interfészt IAsyncDisposable . - Tartalmaz egy privát
IAsyncDisposable
mezőt,_example
amely inicializálva van a konstruktorban. - A
DisposeAsync
metódus delegálja aDisposeAsyncCore
metódust, és felhívja GC.SuppressFinalize , hogy értesítse a szemétgyűjtőt, hogy a véglegesítőnek nem kell futnia. - Ez egy metódust
DisposeAsyncCore()
tartalmaz, amely meghívja a metódust_example.DisposeAsync()
, és beállítja a mezőt a következőrenull
: . - Ez
DisposeAsyncCore()
a metódus lehetővé teszi azvirtual
alosztályok számára, hogy egyéni viselkedéssel felülbírálják azt.
Lezárt alternatív aszinkron megsemmisítési minta
Ha a implementálási osztály lehet sealed
, a metódus felülírásával implementálhatja az aszinkron rendezési IAsyncDisposable.DisposeAsync() mintát. Az alábbi példa bemutatja, hogyan implementálható az aszinkron ártalmatlanítási minta egy lezárt osztályhoz:
public sealed class SealedExampleAsyncDisposable : IAsyncDisposable
{
private readonly IAsyncDisposable _example;
public SealedExampleAsyncDisposable() =>
_example = new NoopAsyncDisposable();
public ValueTask DisposeAsync() => _example.DisposeAsync();
}
Az előző példában:
- Ez
SealedExampleAsyncDisposable
egy lezárt osztály, amely megvalósítja az interfészt IAsyncDisposable . - A tartalmazó
_example
mező inicializálva vanreadonly
a konstruktorban. - A
DisposeAsync
metódus meghívja a metódust_example.DisposeAsync()
, és implementálja a mintát a tárolómezőn keresztül (kaszkádolt ártalmatlanítás).
Az elidegenítési és az aszinkron megsemmisítési minták implementálása
Előfordulhat, hogy mind az interfészeket, mind a IDisposableIAsyncDisposable felületeket implementálnia kell, különösen akkor, ha az osztály hatóköre tartalmazza az implementációk példányait. Ezzel biztosíthatja, hogy megfelelően kaszkádoltan törölje a hívásokat. Íme egy példaosztály, amely mindkét felületet implementálja, és bemutatja a megfelelő útmutatást a törléshez.
class ExampleConjunctiveDisposableusing : IDisposable, IAsyncDisposable
{
IDisposable? _disposableResource = new MemoryStream();
IAsyncDisposable? _asyncDisposableResource = new MemoryStream();
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(disposing: false);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposableResource?.Dispose();
_disposableResource = null;
if (_asyncDisposableResource is IDisposable disposable)
{
disposable.Dispose();
_asyncDisposableResource = null;
}
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_asyncDisposableResource is not null)
{
await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
}
if (_disposableResource is IAsyncDisposable disposable)
{
await disposable.DisposeAsync().ConfigureAwait(false);
}
else
{
_disposableResource?.Dispose();
}
_asyncDisposableResource = null;
_disposableResource = null;
}
}
A IDisposable.Dispose() megvalósítások és IAsyncDisposable.DisposeAsync() az implementációk egyaránt egyszerű sablonkódok.
A túlterhelési Dispose(bool)
metódusban a IDisposable példány feltételesen el lesz osztva, ha nem null
. A IAsyncDisposable példányt úgy öntötték el, mint IDisposable, és ha nem null
is, akkor az is el lesz vetve. Ezután mindkét példányhoz hozzá lesz rendelve null
.
A metódussal DisposeAsyncCore()
ugyanazt a logikai megközelítést követi. Ha a IAsyncDisposable példány nem null
, a hívása DisposeAsync().ConfigureAwait(false)
vár. Ha a IDisposable példány szintén implementációja IAsyncDisposable, akkor az aszinkron módon is el lesz állítva. Ezután mindkét példányhoz hozzá lesz rendelve null
.
Minden megvalósítás arra törekszik, hogy minden lehetséges eldobható objektumot megsemmisítsen. Ez biztosítja, hogy a tisztítás megfelelően kaszkádolt legyen.
Aszinkron eldobható
Az interfészt megvalósító IAsyncDisposable objektumok megfelelő felhasználásához használja együtt a várt és a kulcsszavakat. Vegye figyelembe az alábbi példát, amelyben az ExampleAsyncDisposable
osztály példányosítva lesz, majd egy await using
utasításba burkolva.
class ExampleConfigureAwaitProgram
{
static async Task Main()
{
var exampleAsyncDisposable = new ExampleAsyncDisposable();
await using (exampleAsyncDisposable.ConfigureAwait(false))
{
// Interact with the exampleAsyncDisposable instance.
}
Console.ReadLine();
}
}
Fontos
ConfigureAwait(IAsyncDisposable, Boolean) A felület bővítménymetódusával IAsyncDisposable konfigurálhatja, hogy a tevékenység folytatása hogyan legyen rendezve az eredeti környezetében vagy ütemezőjén. További információ: ConfigureAwait
ConfigureAwait FAQ.
Olyan helyzetekben, amikor nincs szükség a használatra ConfigureAwait
, az utasítás az await using
alábbiak szerint egyszerűsíthető:
class ExampleUsingStatementProgram
{
static async Task Main()
{
await using (var exampleAsyncDisposable = new ExampleAsyncDisposable())
{
// Interact with the exampleAsyncDisposable instance.
}
Console.ReadLine();
}
}
Ezenkívül megírható egy használati deklaráció implicit hatókörének használatára is.
class ExampleUsingDeclarationProgram
{
static async Task Main()
{
await using var exampleAsyncDisposable = new ExampleAsyncDisposable();
// Interact with the exampleAsyncDisposable instance.
Console.ReadLine();
}
}
Több várakozó kulcsszavak egyetlen sorban
Előfordulhat, hogy a await
kulcsszó többször is megjelenik egy sorban. Vegyük például a következő kódot:
await using var transaction = await context.Database.BeginTransactionAsync(token);
Az előző példában:
- A BeginTransactionAsync metódust várjuk.
- A visszatérési típus az DbTransaction, amely megvalósítja
IAsyncDisposable
. - A
transaction
rendszer aszinkron módon használja, és várja is.
Halmozott használat
Olyan helyzetekben, amikor több objektumot hoz létre és használ, amelyek implementálhatókIAsyncDisposable, lehetséges, hogy az utasítások ConfigureAwait halmozása await using
megakadályozhatja a hibás körülmények között érkező DisposeAsync() hívásokat. Annak érdekében, hogy ezt DisposeAsync() mindig meghívja, kerülnie kell a halmozást. Az alábbi három példakód elfogadható mintákat mutat be.
Elfogadható minta
class ExampleOneProgram
{
static async Task Main()
{
var objOne = new ExampleAsyncDisposable();
await using (objOne.ConfigureAwait(false))
{
// Interact with the objOne instance.
var objTwo = new ExampleAsyncDisposable();
await using (objTwo.ConfigureAwait(false))
{
// Interact with the objOne and/or objTwo instance(s).
}
}
Console.ReadLine();
}
}
Az előző példában minden aszinkron törlési művelet explicit hatókörrel rendelkezik a await using
blokk alatt. A külső hatókör azt követi, hogyan objOne
állítja be a kapcsos zárójeleket, és hogyan helyezi el elsőként a kapcsos zárójeleket objTwo
, majd objTwo
a következőt objOne
: . Mindkét IAsyncDisposable
példányra vár a DisposeAsync() metódus, ezért minden példány végrehajtja az aszinkron tisztítási műveletet. A hívások beágyazottak, nem halmozottak.
Elfogadható minta 2
class ExampleTwoProgram
{
static async Task Main()
{
var objOne = new ExampleAsyncDisposable();
await using (objOne.ConfigureAwait(false))
{
// Interact with the objOne instance.
}
var objTwo = new ExampleAsyncDisposable();
await using (objTwo.ConfigureAwait(false))
{
// Interact with the objTwo instance.
}
Console.ReadLine();
}
}
Az előző példában minden aszinkron törlési művelet explicit hatókörrel rendelkezik a await using
blokk alatt. Az egyes blokkok végén a megfelelő IAsyncDisposable
példány metódusa DisposeAsync() vár, így végrehajtja az aszinkron tisztítási műveletet. A hívások szekvenciálisak, nem halmozottak. Ebben a forgatókönyvben objOne
először a megsemmisítés, majd objTwo
a megsemmisítés történik.
Három elfogadható minta
class ExampleThreeProgram
{
static async Task Main()
{
var objOne = new ExampleAsyncDisposable();
await using var ignored1 = objOne.ConfigureAwait(false);
var objTwo = new ExampleAsyncDisposable();
await using var ignored2 = objTwo.ConfigureAwait(false);
// Interact with objOne and/or objTwo instance(s).
Console.ReadLine();
}
}
Az előző példában minden aszinkron tisztítási művelet implicit módon hatókörbe van helyezve a tartalmazó metódus törzsével. A beágyazási blokk végén a példányok elvégzik az IAsyncDisposable
aszinkron tisztítási műveleteket. Ez a példa fordított sorrendben fut, amelyből deklarálták őket, ami azt jelenti, hogy objTwo
a program korábban objOne
elveti őket.
Elfogadhatatlan minta
A következő kód kiemelt sorai azt mutatják, hogy mit jelent a "halmozott használat". Ha a konstruktor kivételt AnotherAsyncDisposable
jelez, egyik objektum sem lesz megfelelően megsemmisítve. A változó objTwo
soha nem lesz hozzárendelve, mert a konstruktor nem fejeződött be sikeresen. Ennek eredményeképpen a konstruktor AnotherAsyncDisposable
felelős a kivétel kiosztása előtt lefoglalt erőforrások eltávolításáért. Ha a ExampleAsyncDisposable
típus rendelkezik véglegesítővel, jogosult a véglegesítésre.
class DoNotDoThisProgram
{
static async Task Main()
{
var objOne = new ExampleAsyncDisposable();
// Exception thrown on .ctor
var objTwo = new AnotherAsyncDisposable();
await using (objOne.ConfigureAwait(false))
await using (objTwo.ConfigureAwait(false))
{
// Neither object has its DisposeAsync called.
}
Console.ReadLine();
}
}
Tipp.
Kerülje ezt a mintát, mert az váratlan viselkedéshez vezethet. Ha az elfogadható minták egyikét használja, a nem feltárt objektumok problémája nem létezik. A tisztítási műveletek helyesen lesznek végrehajtva, ha using
az utasítások nincsenek halmozva.
Lásd még
A gitHubon található forráskódban kettős megvalósítási IDisposable
IAsyncDisposable
példát találhatUtf8JsonWriter.
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: