Aracılığıyla paylaş


Temizleme Kalıbı

Uyarı

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, GC tarafından nesnenin belleği geri kazanılmadan önce çağrılan ve yönetilmeyen kaynakları serbest bırakmak için geçersiz kılınabilen sanal bir yöntem (sonlandırıcı olarak da adlandırılır) Finalize 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ştiricinin ihtiyaç duyulmadığı anda yönetilmeyen kaynakları serbest bırakması için manuel bir yöntem sağlayan ve uygulanması gereken System.IDisposable arabirimini sağlar. Ayrıca, GC'ye bir nesnenin elle bertaraf edildiğini ve artık sonlandırılmasına gerek olmadığını, böylece nesnenin belleğinin daha önce geri kazanılabileceğini söyleyebilen bir yöntem sağlar GC.SuppressFinalize. IDisposable arabirimini uygulayan türler, atılabilir türler olarak adlandırılır.

Dispose Deseni, sonlandırıcılar ve IDisposable arayüzünün kullanımını ve uygulanmasını standartlaştırmak amacıyla tasarlanmıştır.

Desenin temel motivasyonu Finalize ve Dispose yöntemlerinin uygulanmasının 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 Silme Deseni konusundaki ayrıntılar için ilgili bölüme 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.

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

Örneğin, örüntü, yönetilmeyen bellek arabelleklerini depolayan 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 bu tür kaynaklara sahip alt türlere sahip olacak sınıflarda Temel İmha 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ı, System.IDisposable arabirimini uygulamayı ve Dispose(bool) yöntemini bildirirken, kaynak temizleme mantığını hem Dispose yöntemi hem de isteğe bağlı sonlandırıcı ile paylaşacak şekilde düzenlemeyi 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. Metot hem sonlandırıcıdan hem de IDisposable.Dispose yönteminden çağrılır. 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ın nasıl uygulandığına dair ayrıntılar bir sonraki bölümde açıklanmıştır.

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

IDisposable arabirimini uygulayın ve Dispose(true) ardından GC.SuppressFinalize(this) çağırın.

SuppressFinalize çağrısı yalnızca Dispose(true) başarıyla yürütüldüğünde 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) { ... }
}

BILDIRMEYİN yalnızca Dispose ve Dispose() ile sınırlı olan Dispose(bool) yönteminin aşırı yüklemelerini.

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önteminDispose(bool) birden çok kez çağrılmasına izin verin. Metod, ilk çağrıdan sonra hiçbir şey yapmamayı seçebilir.

public class DisposableResourceHolder : IDisposable {

    bool disposed = false;

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

X içinden istisna fırlatmaktanDispose(bool) yalnızca içeren işlemin bozulduğu ve kritik durumların meydana geldiği zamanlarda (sızıntılar, tutarsız paylaşılan durumlar) kaçının.

Kullanıcılar Dispose çağrısı yapıldığında özel durum oluşmamasını bekler.

Bir istisnanın ortaya çıkmasına neden olabilirse Dispose, daha fazla finally bloğu temizleme mantığı yürütülmeyecektir. Bu durumu aşmak için, kullanıcının her Dispose çağrısını (finally bloğu içinde!) bir deneme bloğunda sarması, bu da çok karmaşık temizleme işleyicilerine yol açar. Eğer bir Dispose(bool disposing) yöntem yürütülüyorsa ve disposing yanlışsa, asla bir istisna fırlatmayın. Bunu yaptığınızda, eğer sonlandırıcı bağlamda çalıştırılıyorsa süreç sonlandırılır.

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

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öntemi sağlamayıClose() düşünebilirsiniz, eğer yakın ifade, bu alanda standart bir terminoloji ise Dispose()'ye ek olarak.

Bunu yaparken, `Close` uygulamasını `Dispose` ile özdeş hale getirmeniz ve `IDisposable.Dispose` yöntemini açıkça uygulamayı düşünmeniz önemlidir.

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

Sonlandırılabilir Türler

Sonlandırılabilecek türler, sonlandırıcıyı geçersiz kılarak ve Dispose(bool) yönteminde sonlandırma kodu yolu sağlayarak Temel Yok Etme Deseni'ni 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 Finalize yöntemi için değil, sonlandırıcıdan çağrılan herhangi bir kod için de geçerli olduğunu unutmayın. Önceden tanımlanmış Temel Bertaraf Deseni durumunda, Dispose(bool disposing) parametresi false olduğunda disposing içerisinde 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, sadece ek kaynak temizleme mantığı sağlamak için Dispose(bool) yöntemini geçersiz kılmanız 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. Performans ve kod karmaşıklığı açısından, sonlandırıcı içeren örneklerle ilişkili gerçek bir maliyet vardır. Yönetilmeyen kaynakları kapsüllemek için mümkün olduğunda SafeHandle gibi kaynak sarmalayıcıları kullanmayı tercih edin. Bu durumda, sarmalayıcı kendi kaynak temizliğinden sorumlu olduğu için bir sonlandırıcıya gerek kalmaz.

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

CLR tarafından yalnızca başvuru türleri gerçekten sonlandırılır ve bu nedenle bir değer türüne sonlandırıcı yerleştirme girişimi göz ardı edilir. C# ve C++ derleyicileri bu kuralı uygulatır.

Türün, kendi sonlandırıcısı olmayan yönetilmeyen bir kaynağı serbest bırakmaktan sorumlu olduğu durumlarda, türü sonlandırılabilir hale getirin.

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

public class ComplexResourceHolder : IDisposable {

    ~ComplexResourceHolder() {
        Dispose(false);
    }

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

✓ Her sonlandırılabilir türe Temel Atma Deseni'ni uygulayın.

Bu, türdeki kullanıcılara, sonlandırıcının sorumlu olduğu aynı kaynaklar için belirleyici bir temizlemeyi açıkça gerçekleştirebilmeleri adına 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, diğer bir sonlandırılabilir nesne B'ye referansı olan sonlandırılabilir bir A nesnesi, A'nın sonlandırıcısında B'yi güvenilir bir şekilde kullanamaz veya tam tersi, B'nin sonlandırıcısı da A'yı güvenilir bir şekilde kullanamaz. 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. Environment.HasShutdownStarted true dönerse, son haline getirilebilir bir nesneye başvuran bir statik değişkene erişmek (veya statik değişkenlerde depolanan değerleri kullanabilecek statik bir yöntemi çağırmak) güvenli olmayabilir.

✓ Mutlaka 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-kritik hatalar dışında istisnaların finalizer mantığından kaçmasına izin vermeyin.

Eğer bir sonlandırıcıdan bir özel durum fırlatılırsa, CLR tüm işlemi kapatır (.NET Framework sürüm 2.0'dan itibaren) ve diğer sonlandırıcıların yürütülmesini, kaynakların ise denetimli bir şekilde serbest bırakılmasını engeller.

✓ 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 tür hiyerarşisine sahip bir tür) oluşturmayı ve kullanmayı CriticalFinalizerObject.

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

Pearson Education, Inc. tarafından Krzysztof Cwalina ve Brad Abrams'ın Yeniden Kullanılabilir .NET Kütüphaneleri için Çerçeve Tasarım Yönergeleri: Sözleşmeler, Deyimler ve Kalıplar, 2. Baskı eserinden izniyle yeniden basılmıştır. Addison-Wesley Professional tarafından Microsoft Windows Geliştirme Serisi kapsamında 22 Ekim 2008'de yayımlanmıştır.

Ayrıca bakınız