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 isesealedDisposeAsyncCore(), 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 _exampleiçerir.
  • DisposeAsync yöntemine temsilcilik eder ve sonlandırıcının DisposeAsyncCore ç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ı nullolarak ayarlayan bir DisposeAsyncCore() 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 alan readonly 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 nullkoşullu olarak atılır. Örneği IAsyncDisposable olarak IDisposableyayınlanır ve aynı zamanda değilse nullde atılır. Her iki örnek de öğesine nullatanı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 nullatanı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 ConfigureAwaitdaha 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 şeklindedirDbTransactionIAsyncDisposable.
  • 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 objOneolur. 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 objOneatı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 IAsyncDisposableikili uygulama örneği IDisposable için GitHub'da kaynak koduna Utf8JsonWriter bakın.