DisposeAsync metodu uygulama
Arabirim System.IAsyncDisposable , C# 8.0'ın bir parçası olarak tanıtıldı. Bir Dispose yöntemi uygularken yaptığınız gibi kaynak temizleme gerçekleştirmeniz gerektiğinde yöntemini uygularsınızIAsyncDisposable.DisposeAsync(). Ancak temel farklardan biri, bu uygulamanın zaman uyumsuz temizleme işlemlerine izin vermesidir. , DisposeAsync() zaman uyumsuz elden çıkarma işlemini temsil eden bir ValueTask döndürür.
Sınıfların arabirimini IAsyncDisposable de uyguladığı arabirimi uygularken tipiktir IDisposable . Arabirimin IAsyncDisposable iyi bir uygulama düzeni, zaman uyumlu veya zaman uyumsuz elden çıkarma için hazırlıklı olmaktır, ancak bu bir gereksinim değildir. Sınıfınızın zaman uyumlu bir atılabilir öğesi mümkün değilse, yalnızca IAsyncDisposable sahip olmak kabul edilebilir. Elden çıkarma desenini uygulamaya yönelik tüm yönergeler zaman uyumsuz uygulama için de geçerlidir. Bu makalede Dispose yönteminin nasıl uygulandığını zaten bildiğiniz varsayılır.
Dikkat
Arabirimi uygular ancak arabirimi uygulamazsanız IAsyncDisposable IDisposable uygulamanız kaynakları sızıntıya neden olabilir. Bir sınıf uygular IAsyncDisposable, ancak uygulamazsa IDisposableve bir tüketici yalnızca öğesini çağırırsa Dispose
, uygulamanız hiçbir zaman çağrılmazdı DisposeAsync
. Bu, kaynak sızıntısına neden olur.
İpucu
Bağımlılık ekleme ile ilgili olarak, hizmetleri bir IServiceCollection'a kaydederken hizmet ömrü sizin adınıza örtük olarak yönetilir. IServiceProvider ve karşılık gelen IHost kaynak temizlemeyi düzenler. Özel olarak, ve IAsyncDisposable uygulamaları IDisposable belirtilen yaşam süresi sonunda düzgün bir şekilde atılır.
Daha fazla bilgi için bkz . .NET'te bağımlılık ekleme.
Keşfetme DisposeAsync
ve DisposeAsyncCore
yöntemler
Arabirimi tek IAsyncDisposable bir parametresiz yöntem bildirir: DisposeAsync(). Korumasız herhangi bir sınıf, bir de döndüren bir DisposeAsyncCore()
ValueTaskyöntem tanımlamalıdır.
public
IAsyncDisposable.DisposeAsync() Parametresi olmayan bir uygulama.İmzası
protected virtual ValueTask DisposeAsyncCore()
şu şekilde olan bir yöntem:protected virtual ValueTask DisposeAsyncCore() { }
DisposeAsync
yöntemi
public
Parametresiz DisposeAsync()
yöntem bir await using
deyimde örtük olarak çağrılır ve amacı yönetilmeyen kaynakları boşaltmak, genel temizleme gerçekleştirmek ve varsa sonlandırıcının çalıştırılmaması gerektiğini belirtmektir. Yönetilen nesneyle ilişkili belleği boşaltmak her zaman çöp toplayıcının etki alanıdır. Bu nedenle, standart bir uygulaması vardır:
public async ValueTask DisposeAsync()
{
// Perform async cleanup.
await DisposeAsyncCore();
// Dispose of unmanaged resources.
Dispose(false);
// Suppress finalization.
GC.SuppressFinalize(this);
}
Not
Zaman uyumsuz atma düzeninde dispose desenine kıyasla birincil farklardan biri, aşırı yükleme yöntemine yapılan çağrının DisposeAsync() Dispose(bool)
bağımsız değişken olarak verilmesidir false
. Ancak yöntemi uygulanırken IDisposable.Dispose() bunun true
yerine geçirilir. Bu, zaman uyumlu atma deseniyle işlevsel denklik sağlamaya yardımcı olur ve sonlandırıcı kod yollarının hala çağrılmasını sağlar. Başka bir deyişle, DisposeAsyncCore()
yöntemi yönetilen kaynakları zaman uyumsuz olarak atar, bu nedenle bunları da zaman uyumlu bir şekilde atmak istemezsiniz. Bu nedenle, yerine Dispose(true)
çağrısı Dispose(false)
yapın.
DisposeAsyncCore
yöntemi
yöntemi, DisposeAsyncCore()
yönetilen kaynaklarda zaman uyumsuz temizleme gerçekleştirmeye veya öğesine basamaklı çağrılar DisposeAsync()
yapmaya yöneliktir. Bir alt sınıf uygulaması IAsyncDisposableolan bir temel sınıfı devraldığında ortak zaman uyumsuz temizleme işlemlerini kapsüller. DisposeAsyncCore()
yöntemi, virtual
türetilmiş sınıfların geçersiz kılmalarında özel temizleme tanımlayabilmesini sağlar.
İpucu
uygulaması IAsyncDisposable isesealed
DisposeAsyncCore()
, yöntemi gerekli değildir ve zaman uyumsuz temizleme doğrudan yönteminde IAsyncDisposable.DisposeAsync() gerçekleştirilebilir.
Zaman uyumsuz atma desenini uygulama
Tüm korumasız sınıflar devralınabilecekleri için olası bir temel sınıf olarak kabul edilmelidir. Olası herhangi bir temel sınıf için zaman uyumsuz atma desenini protected virtual ValueTask DisposeAsyncCore()
uygularsanız yöntemini sağlamanız gerekir. Aşağıdaki örneklerden bazıları aşağıdaki gibi tanımlanan bir NoopAsyncDisposable
sınıf kullanır:
public sealed class NoopAsyncDisposable : IAsyncDisposable
{
ValueTask IAsyncDisposable.DisposeAsync() => ValueTask.CompletedTask;
}
Aşağıda, türünü kullanan zaman uyumsuz atma deseninin örnek bir uygulaması verilmiş.NoopAsyncDisposable
türü döndürerek ValueTask.CompletedTaskuygularDisposeAsync
.
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;
}
}
Yukarıdaki örnekte:
ExampleAsyncDisposable
, arabirimini uygulayan korumasız bir sınıftırIAsyncDisposable.- Oluşturucuda başlatılan özel
IAsyncDisposable
bir alan_example
içerir. DisposeAsync
yöntemine temsilcilik eder ve sonlandırıcınınDisposeAsyncCore
çalıştırılması gerekmeyen çöp toplayıcıyı bilgilendirmek için öğesini çağırırGC.SuppressFinalize.- yöntemini çağıran
_example.DisposeAsync()
ve alanınınull
olarak ayarlayan birDisposeAsyncCore()
yöntem içerir. DisposeAsyncCore()
yöntemi, alt sınıfların bunu özel davranışla geçersiz kılmasına olanak tanıyan yöntemidirvirtual
.
Korumalı alternatif zaman uyumsuz atma düzeni
Uygulayan sınıfınız olabilirse sealed
, yöntemini geçersiz kılarak zaman uyumsuz atma desenini IAsyncDisposable.DisposeAsync() uygulayabilirsiniz. Aşağıdaki örnekte, korumalı bir sınıf için zaman uyumsuz atma deseninin nasıl uygulanacakları gösterilmektedir:
public sealed class SealedExampleAsyncDisposable : IAsyncDisposable
{
private readonly IAsyncDisposable _example;
public SealedExampleAsyncDisposable() =>
_example = new NoopAsyncDisposable();
public ValueTask DisposeAsync() => _example.DisposeAsync();
}
Yukarıdaki örnekte:
SealedExampleAsyncDisposable
, arabirimini uygulayan korumalı bir sınıftırIAsyncDisposable.- İçeren
_example
alanreadonly
oluşturucuda başlatılır ve şeklindedir. DisposeAsync
yöntemi, deseni_example.DisposeAsync()
içeren alan (basamaklı atma) aracılığıyla uygulayarak yöntemini çağırır.
Hem atma hem de zaman uyumsuz atma desenlerini uygulama
Özellikle sınıf kapsamınız bu uygulamaların örneklerini içerdiğinde hem hem de IDisposable IAsyncDisposable arabirimlerini uygulamanız gerekebilir. Bunun yapılması, aramaları düzgün bir şekilde basamaklamanızı sağlar. Burada, her iki arabirimi de uygulayan ve temizleme için uygun kılavuzu gösteren bir örnek sınıf verilmiştir.
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;
}
}
IDisposable.Dispose() ve IAsyncDisposable.DisposeAsync() uygulamaları basit ortak kodlardır.
Dispose(bool)
aşırı yükleme yönteminde IDisposable örneği değilse null
koşullu olarak atılır. Örneği IAsyncDisposable olarak IDisposableyayınlanır ve aynı zamanda değilse null
de atılır. Her iki örnek de öğesine null
atanır.
yöntemiyle DisposeAsyncCore()
aynı mantıksal yaklaşım izlenir. IAsyncDisposable Örnek değilse null
çağrısı DisposeAsync().ConfigureAwait(false)
beklenir. IDisposable Örnek aynı zamanda uygulamasının IAsyncDisposableda bir uygulamasıysa, zaman uyumsuz olarak atılır. Her iki örnek de öğesine null
atanır.
Her uygulama, tüm olası atılabilir nesneleri atmaya çalışır. Bu, temizleme işleminin düzgün bir şekilde basamaklanmasını sağlar.
Zaman uyumsuz tek kullanımlık kullanma
Arabirimini uygulayan IAsyncDisposable bir nesneyi düzgün bir şekilde kullanmak için await ve anahtar sözcükleri birlikte kullanırsınız. Sınıfın örneğini oluşturup bir await using
deyimine ExampleAsyncDisposable
sarmalandığı aşağıdaki örneği göz önünde bulundurun.
class ExampleConfigureAwaitProgram
{
static async Task Main()
{
var exampleAsyncDisposable = new ExampleAsyncDisposable();
await using (exampleAsyncDisposable.ConfigureAwait(false))
{
// Interact with the exampleAsyncDisposable instance.
}
Console.ReadLine();
}
}
Önemli
ConfigureAwait(IAsyncDisposable, Boolean) Görevin devamının IAsyncDisposable özgün bağlamı veya zamanlayıcıda nasıl sıralanmış olduğunu yapılandırmak için arabiriminin uzantı yöntemini kullanın. hakkında ConfigureAwait
daha fazla bilgi için bkz . ConfigureAwait SSS.
kullanımına ConfigureAwait
gerek duyulmadığı durumlarda deyimi await using
aşağıdaki gibi basitleştirilebilir:
class ExampleUsingStatementProgram
{
static async Task Main()
{
await using (var exampleAsyncDisposable = new ExampleAsyncDisposable())
{
// Interact with the exampleAsyncDisposable instance.
}
Console.ReadLine();
}
}
Ayrıca, bir using bildiriminin örtük kapsamını kullanmak için yazılabilir.
class ExampleUsingDeclarationProgram
{
static async Task Main()
{
await using var exampleAsyncDisposable = new ExampleAsyncDisposable();
// Interact with the exampleAsyncDisposable instance.
Console.ReadLine();
}
}
Tek bir satırda birden çok await anahtar sözcüğü
await
Bazen anahtar sözcük tek bir satır içinde birden çok kez görünebilir. Örneğin, aşağıdaki kodu göz önünde bulundurun:
await using var transaction = await context.Database.BeginTransactionAsync(token);
Yukarıdaki örnekte:
- BeginTransactionAsync yöntemi bekleniyor.
- dönüş türü, uygulamasını uygulayan şeklindedirDbTransaction
IAsyncDisposable
. transaction
zaman uyumsuz olarak kullanılır ve ayrıca bekler.
Yığılmış kullanılanlar
uygulayan IAsyncDisposablebirden çok nesne oluşturup kullandığınız durumlarda, ile ConfigureAwait yığınlama await using
deyimleri hatalı koşullarda çağrısı yapılmasını DisposeAsync() engelleyebilir. Her zaman çağrıldığından DisposeAsync() emin olmak için yığınlamaktan kaçınmanız gerekir. Aşağıdaki üç kod örneği bunun yerine kullanılacak kabul edilebilir desenleri gösterir.
Kabul edilebilir desen bir
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();
}
}
Yukarıdaki örnekte, her zaman uyumsuz temizleme işleminin kapsamı açıkça bloğun await using
altındadır. Dış kapsam, küme ayraçlarını nasıl objOne
ayarladığına uyar, objTwo
örneğin objTwo
, önce atılır ve ardından objOne
olur. Her iki IAsyncDisposable
örneğin DisposeAsync() de yöntemi bekleneceğinden, her örnek zaman uyumsuz temizleme işlemini gerçekleştirir. Çağrılar yığılmış değil iç içe yerleştirilmiş.
Kabul edilebilir desen iki
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();
}
}
Yukarıdaki örnekte, her zaman uyumsuz temizleme işleminin kapsamı açıkça bloğun await using
altındadır. Her bloğun sonunda, karşılık gelen IAsyncDisposable
örneğin DisposeAsync() yöntemi beklenildiğinden, zaman uyumsuz temizleme işlemi gerçekleştirilir. Çağrılar sıralı, yığılmış değil. Bu senaryoda objOne
önce atılır, sonra objTwo
atılır.
Kabul edilebilir desen üç
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();
}
}
Yukarıdaki örnekte, her zaman uyumsuz temizleme işleminin kapsamı örtük olarak içeren yöntem gövdesiyle belirlenmiştir. Kapsayan bloğun sonunda, IAsyncDisposable
örnekler zaman uyumsuz temizleme işlemlerini gerçekleştirir. Bu örnek, bildirildiği ters sırada çalışır; yani objTwo
öncesinde objOne
atılır.
Kabul edilemez desen
Aşağıdaki kodda vurgulanan satırlar , "yığılmış kullanımın" ne anlama geldiğini gösterir. Oluşturucudan AnotherAsyncDisposable
bir özel durum oluşturulursa, her iki nesne de düzgün bir şekilde atılır. Oluşturucu başarıyla tamamlanamadığından değişken objTwo
hiçbir zaman atanmadı. Sonuç olarak oluşturucu, AnotherAsyncDisposable
özel durum oluşturmadan önce ayrılan kaynakları yok etme sorumluluğundadır. Türün ExampleAsyncDisposable
sonlandırıcısı varsa, sonlandırma için uygundur.
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();
}
}
İpucu
Beklenmeyen davranışlara yol açabileceği için bu desenden kaçının. Kabul edilebilir desenlerden birini kullanırsanız, bozulmamış nesne sorunu mevcut değildir. Deyimler yığılmadığında using
temizleme işlemleri doğru şekilde gerçekleştirilir.
Ayrıca bkz.
ve IAsyncDisposable
ikili uygulama örneği IDisposable
için GitHub'da kaynak koduna Utf8JsonWriter bakın.