Aracılığıyla paylaş


Dispose Deseni

Not

Bu içerik, Çerçeve Tasarım Yönergeleri: Kurallar, Deyimler ve Yeniden Kullanılabilir .NET Kitaplıkları için Desenler, 2. Sürüm'den Pearson Education, Inc.'in izniyle yeniden yazdırılır. Bu baskı 2008'de yayımlandı ve kitap o zamandan beri üçüncü baskıda tamamen revize edilmiştir. Bu sayfadaki bazı bilgiler güncel olmayabilir.

Tüm programlar yürütme sırasında bellek, sistem tanıtıcıları veya veritabanı bağlantıları gibi bir veya daha fazla sistem kaynağı alır. Geliştiricilerin bu tür sistem kaynaklarını kullanırken dikkatli olmaları gerekir, çünkü bunlar alındıktan ve kullanıldıktan sonra serbest bırakılmalıdır.

CLR, otomatik bellek yönetimi için destek sağlar. Yönetilen belleğin (C# işleci newkullanılarak ayrılan bellek) açıkça serbest bırakılması gerekmez. Çöp toplayıcı (GC) tarafından otomatik olarak serbest bırakılır. Bu, geliştiricileri sıkıcı ve zor bellek bırakma görevinden kurtarıyor ve .NET Framework tarafından karşılanan benzersiz üretkenliğin ana nedenlerinden biri oldu.

Ne yazık ki, yönetilen bellek birçok sistem kaynağı türünden yalnızca biridir. Yönetilen bellek dışındaki kaynakların yine de açıkça yayımlanması gerekir ve yönetilmeyen kaynaklar olarak adlandırılır. GC, bu tür yönetilmeyen kaynakları yönetmek için özel olarak tasarlanmamıştır. Bu da yönetilmeyen kaynakları yönetme sorumluluğunun geliştiricilerin elinde olduğu anlamına gelir.

CLR, yönetilmeyen kaynakları serbest bırakma konusunda bazı yardım sağlar. System.Object nesnenin belleği GC tarafından geri kazanılmadan önce GC tarafından çağrılan ve yönetilmeyen kaynakları serbest bırakmak için geçersiz kılınabilen bir sanal yöntem Finalize (sonlandırıcı olarak da adlandırılır) bildirir. Sonlandırıcıyı geçersiz kılan türler sonlanabilir türler olarak adlandırılır.

Sonlandırıcılar bazı temizleme senaryolarında etkili olsa da, iki önemli dezavantajı vardır:

  • Sonlandırıcı, GC bir nesnenin koleksiyon için uygun olduğunu algıladığında çağrılır. Bu, kaynağın artık gerekli olmamasının ardından belirli bir süre sonra gerçekleşir. Geliştiricinin kaynağı serbest bırakabileceği veya yayınlamak istemesi ile kaynağın sonlandırıcı tarafından gerçekten yayınlandığı zaman arasındaki gecikme, çok az sayıda kaynak (kolayca tüketilebilen kaynaklar) alan programlarda veya kaynakların kullanımda tutulmasının maliyetli olduğu durumlarda (büyük yönetilmeyen bellek arabellekleri gibi) kabul edilemez.

  • CLR'nin bir sonlandırıcı çağırması gerektiğinde, nesnenin belleğinin toplanmasını bir sonraki çöp toplama turuna kadar ertelemesi gerekir (sonlandırıcılar koleksiyonlar arasında çalışır). Bu, nesnenin belleğinin (ve başvurduğu tüm nesnelerin) daha uzun bir süre serbest bırakılmayacağı anlamına gelir.

Bu nedenle, yönetilmeyen kaynakları mümkün olan en kısa sürede geri kazanmanın önemli olduğu, kıt kaynaklarla uğraşırken veya eklenen GC sonlandırma ek yükünün kabul edilemez olduğu yüksek performanslı senaryolarda yalnızca sonlandırıcılara güvenmek pek çok senaryoda uygun olmayabilir.

Çerçeve, geliştiriciye System.IDisposable yönetilmeyen kaynakları ihtiyaç duyulmadığı anda serbest bırakmak için el ile bir yol sağlamak için uygulanması gereken arabirimi sağlar. Ayrıca GC'ye bir nesnenin el ile atıldığını ve artık son haline getirilmemesi gerektiğini ve bu durumda nesnenin belleğinin daha önce geri alınabileceğini söyleyebilen bir yöntem sağlar GC.SuppressFinalize . Arabirimi uygulayan IDisposable türler atılabilir türler olarak adlandırılır.

Dispose Deseni, sonlandırıcıların ve arabirimin kullanımını ve uygulanmasını standartlaştırmaya IDisposable yöneliktir.

Desenin temel motivasyonu ve Dispose yöntemlerinin uygulanmasının Finalize karmaşıklığını azaltmaktır. Karmaşıklık, yöntemlerin tüm kod yollarını paylaşmamasından kaynaklanır (farklılıklar bölümün ilerleyen bölümlerinde açıklanmıştır). Ayrıca, belirleyici kaynak yönetimi için dil desteğinin evrimi ile ilgili desenin bazı öğelerinin geçmişe dönük nedenleri vardır.

✓ Atılabilir türlerin örneklerini içeren türlerde Temel Atma Desenini uygulayın. Temel desenin ayrıntıları için Temel Atma Düzeni bölümüne bakın.

Bir tür diğer atılabilir nesnelerin kullanım ömründen sorumluysa, geliştiricilerin de bunları atacak bir yönteme ihtiyacı vardır. Kapsayıcının Dispose yöntemini kullanmak, bunu mümkün hale getirmenin kullanışlı bir yoludur.

✓ Temel Atma Desenini uygulayın ve açıkça serbest bırakılması gereken ve sonlandırıcıları olmayan kaynakları tutan türlerde sonlandırıcı sağlayın.

Örneğin, desen yönetilmeyen bellek arabelleklerini depolanan türlerde uygulanmalıdır. Sonlandırılabilir Türler bölümünde sonlandırıcıları uygulamayla ilgili yönergeler ele alınmaktadır.

✓ Yönetilmeyen kaynakları veya atılabilir nesneleri barındırmayan ancak büyük olasılıkla alt türleri olan sınıflarda Temel Atma Desenini uygulamayı düşünün .

Bunun harika bir örneği sınıfıdır System.IO.Stream . Hiçbir kaynağı barındırmayan soyut bir temel sınıf olsa da, alt sınıflarının çoğu bunu yapar ve bu nedenle bu deseni uygular.

Temel Atma Düzeni

Desenin temel uygulaması, arabirimini System.IDisposable uygulamayı ve yöntemle isteğe bağlı sonlandırıcı arasında paylaşılacak tüm kaynak temizleme mantığını uygulayan yöntemi bildirmeyi Dispose(bool)Dispose içerir.

Aşağıdaki örnekte temel desenin basit bir uygulaması gösterilmektedir:

public class DisposableResourceHolder : IDisposable {

    private SafeHandle resource; // handle to a resource

    public DisposableResourceHolder() {
        this.resource = ... // allocates the resource
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing) {
        if (disposing) {
            if (resource!= null) resource.Dispose();
        }
    }
}

Boole parametresi disposing yöntemin uygulamadan IDisposable.Dispose mı yoksa sonlandırıcıdan mı çağrıldığı gösterir. Uygulamanın Dispose(bool) , diğer başvuru nesnelerine erişmeden önce parametresini denetlemesi gerekir (örneğin, önceki örnekteki kaynak alanı). Bu tür nesnelere yalnızca yöntem uygulamadan IDisposable.Dispose çağrıldığında (parametre true'ya disposing eşit olduğunda) erişilmelidir. Yöntem sonlandırıcıdan çağrılırsa (disposing false ise), diğer nesnelere erişilmemelidir. Bunun nedeni, nesnelerin tahmin edilemeyen bir sırada son halini almalarıdır ve bu nedenle bunlar veya bağımlılıklarından herhangi biri zaten sonlandırılmış olabilir.

Ayrıca bu bölüm, Dispose Desenini henüz uygulamayan bir temele sahip sınıflar için de geçerlidir. Deseni zaten uygulayan bir sınıftan devralıyorsanız, ek kaynak temizleme mantığı sağlamak için yöntemini geçersiz kılmanız Dispose(bool) yeterlidir.

✓ Yönetilmeyen kaynakların serbest bırakılmasıyla ilgili tüm mantığı merkezileştirmek için bir protected virtual void Dispose(bool disposing) yöntem bildirin.

Tüm kaynak temizleme işlemi bu yöntemde gerçekleşmelidir. yöntemi hem sonlandırıcıdan hem de yöntemden çağrılır IDisposable.Dispose . Bir sonlandırıcının içinden çağrılırsa parametresi false olur. Sonlandırma sırasında çalışan tüm kodların diğer sonlandırılabilir nesnelere erişmediğinden emin olmak için kullanılmalıdır. Sonlandırıcıları uygulama ayrıntıları sonraki bölümde açıklanmıştır.

protected virtual void Dispose(bool disposing) {
    if (disposing) {
        if (resource!= null) resource.Dispose();
    }
}

✓ Sadece çağırarak Dispose(true) arabirimi uygulayın IDisposable ve takip edinGC.SuppressFinalize(this).

çağrısı SuppressFinalize yalnızca başarıyla yürütülürse Dispose(true) gerçekleşmelidir.

public void Dispose(){
    Dispose(true);
    GC.SuppressFinalize(this);
}

X parametresiz Dispose yöntemi sanal YAPMAYIN.

Dispose(bool) yöntemi, alt sınıflar tarafından geçersiz kılınması gereken yöntemdir.

// bad design
public class DisposableResourceHolder : IDisposable {
    public virtual void Dispose() { ... }
    protected virtual void Dispose(bool disposing) { ... }
}

// good design
public class DisposableResourceHolder : IDisposable {
    public void Dispose() { ... }
    protected virtual void Dispose(bool disposing) { ... }
}

X ve dışında Dispose(bool)Dispose() yöntemin Dispose aşırı yüklemelerini BILDIRMEYİN.

Dispose bu düzenin daha iyi bir şekilde birleştirilmesine yardımcı olmak ve uygulayıcılar, kullanıcılar ve derleyiciler arasındaki karışıklığı önlemek için ayrılmış bir sözcük olarak kabul edilmelidir. Bazı diller bu deseni belirli türlerde otomatik olarak uygulamayı seçebilir.

✓ Yöntemin Dispose(bool) birden çok kez çağrılmasına izin verin. yöntemi ilk çağrıdan sonra hiçbir şey yapmayı seçebilir.

public class DisposableResourceHolder : IDisposable {

    bool disposed = false;

    protected virtual void Dispose(bool disposing) {
        if (disposed) return;
        // cleanup
        ...
        disposed = true;
    }
}

X İçeren işlemin bozuk olduğu kritik durumlar (sızıntılar, tutarsız paylaşılan durum vb.) dışında içinden bir özel durum Dispose(bool) oluşturmaktan KAÇıNıN.

Kullanıcılar çağrısının Dispose özel durum oluşturmamasını bekler.

Bir özel durum tetiklenebilirse Dispose , daha fazla finally-block temizleme mantığı yürütülmeyecektir. Bu sorunu geçici olarak çözmek için kullanıcının her çağrıyı Dispose (finally bloğunda!) bir deneme bloğunda sarmalayıp çok karmaşık temizleme işleyicilerine yol açması gerekir. Bir Dispose(bool disposing) yöntem yürütülüyorsa, atma yanlışsa hiçbir zaman bir özel durum oluşturma. Bunu yaptığınızda, sonlandırıcı bağlam içinde yürütülürse işlem sonlandırılır.

✓ Nesne atıldıktan sonra kullanılamayan herhangi bir üyeden bir ObjectDisposedException atmayın.

public class DisposableResourceHolder : IDisposable {
    bool disposed = false;
    SafeHandle resource; // handle to a resource

    public void DoSomething() {
        if (disposed) throw new ObjectDisposedException(...);
        // now call some native methods using the resource
        ...
    }
    protected virtual void Dispose(bool disposing) {
        if (disposed) return;
        // cleanup
        ...
        disposed = true;
    }
}

✓ Yöntem sağlamayı Close()DÜŞÜNÜN, buna ek olarakDispose(), yakın alanda standart terminoloji ise.

Bunu yaparken, uygulamayı ile özdeş Dispose hale getirmeniz Close ve yöntemi açıkça uygulamayı IDisposable.Dispose göz önünde bulundurmanız önemlidir.

public class Stream : IDisposable {
    IDisposable.Dispose() {
        Close();
    }
    public void Close() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Sonlandırılabilir Türler

Sonlandırılabilir türler, sonlandırıcıyı geçersiz kılarak ve yönteminde sonlandırma kodu yolu sağlayarak Temel Atma Desenini Dispose(bool) genişleten türlerdir.

Sonlandırıcıların doğru şekilde uygulanması çok zordur, çünkü bunların yürütülmesi sırasında sistemin durumu hakkında belirli (normalde geçerli) varsayımlarda bulunamazsınız. Aşağıdaki yönergeler dikkatli bir şekilde dikkate alınmalıdır.

Bazı yönergelerin yalnızca yöntem için Finalize değil, sonlandırıcıdan çağrılan kodlar için de geçerli olduğunu unutmayın. Önceden tanımlanmış Temel Atma Düzeni söz konusu olduğunda, bu parametre false olduğunda disposing içinde Dispose(bool disposing) yürütülen mantık anlamına gelir.

Temel sınıf zaten sonlandırılabilirse ve Temel Atma Desenini uygularsa, yeniden geçersiz kılmamalısınız Finalize . Bunun yerine ek kaynak temizleme mantığı sağlamak için yöntemini geçersiz kılmanız Dispose(bool) gerekir.

Aşağıdaki kodda son haline getirilebilir bir tür örneği gösterilmektedir:

public class ComplexResourceHolder : IDisposable {

    private IntPtr buffer; // unmanaged memory buffer
    private SafeHandle resource; // disposable handle to a resource

    public ComplexResourceHolder() {
        this.buffer = ... // allocates memory
        this.resource = ... // allocates the resource
    }

    protected virtual void Dispose(bool disposing) {
        ReleaseBuffer(buffer); // release unmanaged memory
        if (disposing) { // release other disposable objects
            if (resource!= null) resource.Dispose();
        }
    }

    ~ComplexResourceHolder() {
        Dispose(false);
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

X AVOID , türleri sonlandırılabilir hale getirir.

Sonlandırıcının gerekli olduğunu düşündüğünüz her durumda dikkatli bir şekilde düşünün. Hem performans hem de kod karmaşıklığı açısından sonlandırıcıları olan örneklerle ilişkilendirilmiş gerçek bir maliyet vardır. Mümkün olduğunda yönetilmeyen kaynakları kapsüllemek gibi SafeHandle kaynak sarmalayıcıları kullanmayı tercih edin. Bu durumda, sarmalayıcı kendi kaynak temizlemeden sorumlu olduğundan sonlandırıcı gereksiz hale gelir.

X Değer türlerini son haline GETIRMEYİn .

Yalnızca başvuru türleri aslında CLR tarafından sonlandırılır ve bu nedenle bir değer türüne sonlandırıcı yerleştirme girişimleri yoksayılır. C# ve C++ derleyicileri bu kuralı zorlar.

✓ Türün kendi sonlandırıcısı olmayan yönetilmeyen bir kaynağı yayınlamaktan sorumlu olması durumunda türü sonlandırılabilir hale getirin.

Sonlandırıcıyı uygularken, tüm kaynak temizleme mantığını çağırın Dispose(false) ve yönteminin Dispose(bool disposing) içine yerleştirin.

public class ComplexResourceHolder : IDisposable {

    ~ComplexResourceHolder() {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing) {
        ...
    }
}

✓ Her sonleştirilebilir türe Temel Atma Desenini uygulayın.

Bu, türdeki kullanıcılara, sonlandırıcının sorumlu olduğu aynı kaynakların açıkça belirlenici temizlemesini gerçekleştirmek için bir araç sağlar.

X Sonlandırıcı kod yolundaki herhangi bir sonleştirilebilir nesneye ERIŞMEYİn , çünkü bunların zaten sonlandırılmış olması önemli bir risktir.

Örneğin, başka bir son haline getirilebilir B nesnesine başvurusu olan sonleştirilebilir bir A nesnesi, A'nın sonlandırıcısında B'yi güvenilir bir şekilde kullanamaz veya tam tersi de geçerlidir. Sonlandırıcılar rastgele bir sırada çağrılır (kritik sonlandırma için zayıf sıralama garantisinin dışında).

Ayrıca, statik değişkenlerde depolanan nesnelerin uygulama etki alanının kaldırılması sırasında veya işlemden çıkarken belirli noktalarda toplanacağına dikkat edin. Son haline getirilebilir bir nesneye başvuran bir statik değişkene (veya statik değişkenlerde depolanan değerleri kullanabilecek statik bir yöntemi çağırmak) true döndürürse Environment.HasShutdownStarted güvenli olmayabilir.

✓ Yönteminizi Finalize korumalı hale getirin.

Derleyiciler bu yönergeyi uygulamaya yardımcı olduğundan C#, C++ ve VB.NET geliştiricilerinin bu konuda endişelenmesi gerekmez.

X Sistem açısından kritik hatalar dışında özel durumların sonlandırıcı mantığından kaçmasına izin VERMEYİn .

Sonlandırıcıdan bir özel durum oluşturulursa CLR tüm işlemi (.NET Framework sürüm 2.0'dan itibaren) kapatır ve diğer sonlandırıcıların yürütülmesini ve kaynakların denetimli bir şekilde yayımlanmasını önler.

✓ Sonlandırıcının zorunlu uygulama etki alanı yüklemeleri ve iş parçacığı iptalleri karşısında bile kesinlikle yürütülmesi gereken durumlar için kritik bir sonleştirilebilir nesne (içeren CriticalFinalizerObjecttür hiyerarşisine sahip bir tür) oluşturmayı ve kullanmayı GÖZ ÖNÜNDE BULUNDURUN.

Porsiyonlar © 2005, 2009 Microsoft Corporation. Tüm hakları saklıdır.

Pearson Education, Inc. in Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraryes, 2nd Edition by Krzysztof Cwalina and Brad Abrams izniyle yeniden yazdırıldı ve 22 Ekim 2008'de Addison-Wesley Professional tarafından Microsoft Windows Geliştirme Serisi'nin bir parçası olarak yayımlandı.

Ayrıca bkz.