DisposeAsync metodu uygulama
System.IAsyncDisposableArabirim, C# 8,0 'nin bir parçası olarak sunulmuştur. IAsyncDisposable.DisposeAsync() Bir Dispose yöntemi uygularkenyaptığınız gibi, kaynak temizlik gerçekleştirmeniz gerektiğinde yöntemini uygulayabilirsiniz. Ancak, bu uygulamanın zaman uyumsuz temizleme işlemlerine izin verdiği önemli farklılıklardan biridir. , DisposeAsync() ValueTask Zaman uyumsuz Dispose işlemini temsil eden bir döndürür.
Sınıfların de arabirimini uyguladığı arabirim, tipik olarak kullanılır IAsyncDisposable IDisposable . Arabirimin iyi bir uygulama deseninin, IAsyncDisposable zaman uyumlu ya da zaman uyumsuz Dispose için hazırlanmasıdır. Dispose deseninin uygulanması için tüm rehberlik, zaman uyumsuz uygulama için de geçerlidir. Bu makalede, bir Dispose yönteminin nasıl uygulanacağınıbildiğiniz varsayılmaktadır.
İpucu
Bağımlılık ekleme ile ilgili olarak, Hizmetleri bir içinde kaydettirirken IServiceCollection hizmet ömrü sizin adınıza örtülü olarak yönetilir. IServiceProviderVe buna karşılık gelen IHost kaynak temizliği. Özellikle, ve uygulamaları IDisposable , IAsyncDisposable belirtilen yaşam sürelerinin sonuna doğru şekilde atıldı.
Daha fazla bilgi için bkz. .net 'e bağımlılık ekleme.
DisposeAsync () ve Dispodeniz synccore ()
IAsyncDisposableArabirim, parametresiz tek bir yöntem bildirir DisposeAsync() . Korumalı olmayan herhangi bir sınıfın DisposeAsyncCore() , döndüren ek bir yöntemi olmalıdır ValueTask .
Parametresi olmayan bir
publicIAsyncDisposable.DisposeAsync() uygulama.protected virtual ValueTask DisposeAsyncCore()İmzası şu şekilde olan bir yöntem:protected virtual ValueTask DisposeAsyncCore() { }
DisposeAsync () yöntemi
publicParametresiz DisposeAsync() yöntemi bir bildirimde örtük olarak çağrılır await using ve amacı yönetilmeyen kaynakları serbest bırakmak, genel temizleme gerçekleştirmek ve bir tane varsa sonlandırıcının çalıştırılmaması gerektiğini belirtmek için kullanılır. Yönetilen bir nesneyle ilişkili belleği serbest bırakma, her zaman çöp toplayıcınınetki 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);
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
// Suppress finalization.
GC.SuppressFinalize(this);
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
}
Not
Zaman uyumsuz Dispose deseninin, Dispose düzenine kıyasla bir birincil farkı, DisposeAsync() Dispose(bool) aşırı yükleme yöntemine yapılan çağrının false bir bağımsız değişken olarak verilme yöntemidir. IDisposable.Dispose()Ancak, yöntemi uygularken true bunun yerine geçirilir. Bu, zaman uyumlu Dispose düzeniyle işlevsel denklik sağlanmasına yardımcı olur ve Sonlandırıcı kod yollarının hala çağrılmasını sağlar. Diğer bir deyişle, DisposeAsyncCore() Yöntem yönetilen kaynakları zaman uyumsuz olarak elden atıyor, bu nedenle bunları zaman uyumlu olarak atmak istemezsiniz. Bu nedenle, Dispose(false) yerine çağırın Dispose(true) .
Dispomevsimsynccore () yöntemi
DisposeAsyncCore()Yöntemi, yönetilen kaynakların zaman uyumsuz temizliğini veya ' nin basamaklı çağrılarını gerçekleştirmek için tasarlanmıştır DisposeAsync() . Bir alt sınıf, uygulamasının bir temel sınıfını devraldığında ortak zaman uyumsuz temizleme işlemlerini Kapsüller IAsyncDisposable . DisposeAsyncCore()Yöntemi, virtual türetilmiş sınıfların geçersiz Kılmalarda ek temizleme tanımlayabilmeleri için kullanılır.
İpucu
Bir uygulamasına IAsyncDisposable ise sealed , DisposeAsyncCore() yöntemi gerekli değildir ve zaman uyumsuz temizleme doğrudan IAsyncDisposable.DisposeAsync() yönteminde gerçekleştirilebilir.
Zaman uyumsuz Dispose modelini uygulama
Tüm korumalı olmayan sınıflar, devralınabileceğinden olası bir temel sınıf olarak düşünülmelidir. Herhangi bir olası temel sınıf için zaman uyumsuz Dispose modelini uygularsanız, yöntemini sağlamanız gerekir protected virtual ValueTask DisposeAsyncCore() . Tarafından kullanılan zaman uyumsuz Dispose deseninin örnek bir uygulamasıdır System.Text.Json.Utf8JsonWriter .
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
public class ExampleAsyncDisposable : IAsyncDisposable, IDisposable
{
private Utf8JsonWriter _jsonWriter = new(new MemoryStream());
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore().ConfigureAwait(false);
Dispose(disposing: false);
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
GC.SuppressFinalize(this);
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_jsonWriter?.Dispose();
_jsonWriter = null;
}
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_jsonWriter is not null)
{
await _jsonWriter.DisposeAsync().ConfigureAwait(false);
}
_jsonWriter = null;
}
}
Önceki örnekte, kullanılır Utf8JsonWriter . Hakkında daha fazla bilgi için System.Text.Json , bkz. Newtonsoft. JSON 'dan System. Text. JSON 'a geçiş.
Dispose ve Async Dispose desenleri uygulama
IDisposable IAsyncDisposable Özellikle sınıf kapsamınız bu uygulamaların örneklerini içerdiğinde hem hem de arabirimlerini uygulamanız gerekebilir. Bunun yapılması, temizleme çağrılarını doğru bir şekilde basamaklı olarak belirleyebilmenizi sağlar. Her iki arabirimi de uygulayan ve temizleme için uygun Kılavuzu gösteren örnek bir sınıf aşağıda verilmiştir.
using System;
using System.IO;
using System.Threading.Tasks;
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);
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
GC.SuppressFinalize(this);
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposableResource?.Dispose();
(_asyncDisposableResource as IDisposable)?.Dispose();
_disposableResource = null;
_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ı hem basit ortak koddur.
Dispose(bool)Aşırı yükleme yönteminde, IDisposable örnek koşullu olarak atıldı null . IAsyncDisposableÖrnek olarak atama yapılır IDisposable ve aynı zamanda atılkdır null . Her iki örnek de öğesine atanır null .
DisposeAsyncCore()Yöntemiyle aynı mantıksal yaklaşım izlenir. IAsyncDisposableÖrnek değilse null , öğesine çağrısı DisposeAsync().ConfigureAwait(false) beklenir. IDisposableÖrnek aynı zamanda bir uygulama ise IAsyncDisposable , zaman uyumsuz olarak da atılmış olur. Her iki örnek de öğesine atanır null .
Async atılabilir kullanma
Arabirimi uygulayan bir nesneyi düzgün bir şekilde kullanmak için IAsyncDisposable await kullanır ve anahtar sözcükleri birlikte kullanabilirsiniz. Aşağıdaki örneği, ExampleAsyncDisposable sınıfın örneklendiği ve sonra bir deyime sarmalanmış olduğunu göz önünde bulundurun await using .
using System;
using System.Threading.Tasks;
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) IAsyncDisposable Görevin devamlılığın orijinal bağlam veya Scheduler üzerinde nasıl sıraya alınacağını yapılandırmak için arabirimin genişletme yöntemini kullanın. Hakkında daha fazla bilgi için ConfigureAwait bkz. ConfigureAwait SSS.
Kullanımının ConfigureAwait gerekli olmadığı durumlarda, await using aşağıdaki gibi bir ifade basitleştirilir:
using System;
using System.Threading.Tasks;
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.
using System;
using System.Threading.Tasks;
class ExampleUsingDeclarationProgram
{
static async Task Main()
{
await using var exampleAsyncDisposable = new ExampleAsyncDisposable();
// Interact with the exampleAsyncDisposable instance.
Console.ReadLine();
}
}
Tek satırda birden çok await anahtar sözcüğü
Bazen await anahtar sözcüğü tek bir satırda 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:
- BeginTransactionAsyncYöntemi bekletildi.
- Dönüş türü DbTransaction , öğesini uygular
IAsyncDisposable. - ,
transactionZaman uyumsuz olarak kullanılır ve de bekletildi.
Yığılmış kullanımlar
Uygulayan birden çok nesne oluşturup kullandığınız durumlarda IAsyncDisposable , await using ile yığınlama deyimlerinin, ConfigureAwait errant koşullarında çağrıları engelleyebileceği olasıdır DisposeAsync() . DisposeAsync()Her zaman çağrıldığından emin olmak için yığınlama ' ı kullanmaktan kaçının. Aşağıdaki üç kod örneği, yerine kullanılacak kabul edilebilir desenleri gösterir.
Kabul edilebilir tek desenler
using System;
using System.Threading.Tasks;
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şlemi bloğunun altında açıkça kapsamlandırılır await using . Dış kapsam, bu, kapsayan, kapsayan, ve ' objOne objTwo nin ilk olarak nasıl objTwo bırakıldığı, ve ardından tarafından tanımlanır objOne . Her iki IAsyncDisposable DisposeAsync() Örneğin yöntemi bekletildi, bu nedenle her örnek zaman uyumsuz temizleme işlemini gerçekleştirir. Çağrılar yığılır, iç içe değildir.
Kabul edilebilir iki model
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şlemi bloğunun altında açıkça kapsamlandırılır await using . Her bloğun sonunda, karşılık gelen IAsyncDisposable örnek DisposeAsync() yöntemi bekletildi ve bu nedenle zaman uyumsuz temizleme işlemini gerçekleştiriyor. Çağrılar sıralı değildir, yığın değildir. Bu senaryoda objOne önce atıldığından, daha sonra objTwo atılmış olur.
Kabul edilebilir üç desenli
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();
}
}
Önceki örnekte, her zaman uyumsuz temizleme işlemi, kapsayan Yöntem gövdesi ile dolaylı olarak kapsamlıdır. Kapsayan bloğun sonunda, IAsyncDisposable örnekler zaman uyumsuz temizleme işlemlerini gerçekleştirir. Bu, daha önce çıkarılan, bildirildiği sırada çalışır objTwo objOne .
Kabul edilemeyen desenler
Aşağıdaki kodda vurgulanan satırlarda "yığılmış kullanımlar" ne anlama geldiğini gösterilmektedir. Oluşturucudan bir özel durum oluşturulursa AnotherAsyncDisposable , hiçbir nesne düzgün şekilde atılamaz. objTwoOluşturucu başarıyla tamamlanmadığından değişken atanmadı. Sonuç olarak, için Oluşturucusu AnotherAsyncDisposable bir özel durum oluşturmadan önce ayrılan kaynakları elden atma işleminden sorumludur.
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ışa yol açacağından bu kalıbı kullanmaktan kaçının.
Ayrıca bkz.
Ve ikili uygulama örneği için IDisposable IAsyncDisposable Utf8JsonWriter GitHubkaynak koda bakın.