<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 原則17:實現標準的 Dispose 模式 **By D.S.Qiu** **尊重他人的勞動,支持原創,轉載請注明出處:[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 我們已經討論過回收持有非托管資源對象的重要性。現在就介紹怎么實現管理類所包含不是內存的資源的代碼。 .NET 框架回收非托管資源已經有了標準的模式。你的類的使用者會希望你遵循這個標準模式。標準的回收習慣是使用者調用你實現的 IDisposable 接口,如果使用者忘記了析構函數也會被動執行。它和垃圾回收器一起工作,保證你的對象在必要的時候只受到析構函數帶來的性能損失。這就是正確處理非托管資源的方式,所以你需要完全弄明白它。 在繼承層次的基類應該實現 IDisposable 接口以釋放資源。這個類也應添加析構函數作為被動的調用。這兩個方法都是通過虛函數實現資源釋放,以至于子類可以根據它們自己的資源管理需求進行重載。子類只有當需要釋放自己的資源的時候才需要重載虛函數,并且記得調用基類的實現版本。 開始時,如果你的類使用非托管資源,這個類就必須有析構函數。你不應該依賴使用者總是調用 Dispose() 方法。如果他們忘記了你就會出現內存泄露問題。沒有調用 Dispose 是他們的過失,但是你也會受到他們的責備。唯一的保證非托管資源會被正確的釋放的方式是創建析構函數。所以就創建一個。 當垃圾回收器運行時,它會立即把沒有析構函數的對象從內存中移除。有析構函數的所有對象還會留在內存中。這些對象會添加到析構執行隊列中,并且垃圾回收器起一個新線程執行這些對象析構函數。析構線程完成工作后,垃圾對象才會從內存中移除。需要析構的對象在內存中停留的時間會比沒有析構函數的對象久點。但是你別無選擇。如果你的類持有非托管資源,你就得被動實現析構函數。但是你不用太擔心性能。下一步保證用戶使用更簡單,而且可以避免析構函數造成的性能損失。 實現 IDisposable 是標準的方式告訴用戶和系統你的對象持有必須被及時釋放的資源。 IDiposable 接口只包含一個方法: ``` public interface IDisposable { void Dispose(); } ``` 實現 IDisposable.Dispose() 方法要負責四個任務: 1.釋放所有非托管資源。 2.釋放所有托管資源(包括卸載事件)。 3.設置表示對象依據被清理的標記位。在 public 方法你需要堅持這個狀態值,并且如果已經被回收對象的調用要拋出 ObjectDisposed 異常。 4.阻止析構。你通過調用 GC.SuppressFinalize(this) 來完成這項工作。 實現 IDisposable ,你完成兩件事情:第一個你提供了一種及時釋放托管資源的機制,另一個是你提供了標準模式讓用戶釋放非托管資源。這是非常重要的。你為類型實現 IDisposable 后,用戶可以避免析構的開銷。你的類在 .NET 環境中就理所當然有很好的表現。 但是這個機制還是會有些坑。怎么樣做到子類清理自己的資源并且基類同時也進行清理。如果子類重載了析構函數或者添加自己的 IDisposable 實現,這兩個方法必須調用基類的對應的實現。否則,基類就不會被正確的清理。并且,析構函數和 Dispose 有著相同的職責:幾乎可以肯定析構函數和 Dispose 方法中會有重復的代碼。后面你會在原則23中學到,重載接口函數不會像你期待那樣工作。標準模式中的第三個方法,通過一個受保護的輔助性虛函數,提取出它們的常規任務并且讓子類來釋放自己的資源。基類包含接口的核心代碼, 子類提供的 Dispose()虛函數或者析構函數來負責清理資源: ``` protected virtual void Dispose(bool isDisposing) ``` 方法重載這個方法是非常必須的以同時支持析構函數和 Dispose,并且因為它虛函數,可以為所有子類提供入口。子類可以重載這個方法,提供恰當的實現去清理它們的資源,和調用基類的實現。當 isDisposing 為 true 時,你清理托管資源和非托管資源,如果為 false ,你只清理了非托管資源。這兩種情況,都要調用基類的 Dispose(bool) 方法去清理基類的資源。 下面這個簡單例子為你展示如何實現這個模式。 MyResourceHog 類演示實現了 IDisposable 和創建一個虛 Dispose 方法: ``` public class MyResourceHog : IDisposable { // Flag for already disposed private bool alreadyDisposed = false; // Implementation of IDisposable. // Call the virtual Dispose method. // Suppress Finalization. public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // Virtual Dispose method protected virtual void Dispose(bool isDisposing) { // Don't dispose more than once. if (alreadyDisposed) return; if (isDisposing) { // elided: free managed resources here. } // elided: free unmanaged resources here. // Set disposed flag: alreadyDisposed = true; } public void ExampleMethod() { if (alreadyDisposed) throw new ObjectDisposedException("MyResourceHog","Called Example Method on Disposed object"); // remainder elided. } } ``` 如果你一個子類需要額外的清理,可以實現 protected Dispose 方法: ``` public class DerivedResourceHog : MyResourceHog { // Have its own disposed flag. private bool disposed = false; protected override void Dispose(bool isDisposing) { // Don't dispose more than once. if (disposed) return; if (isDisposing) { // TODO: free managed resources here. } // TODO: free unmanaged resources here. // Let the base class free its resources. // Base class is responsible for calling // GC.SuppressFinalize( ) base.Dispose(isDisposing); // Set derived class disposed flag: disposed = true; } } ``` 注意到基類和子類都包含了一個回收狀態的標記位。這完全是沒有必要的。重復的標記可以掩蓋在回收時所有可能出現的錯誤,標記位是相對對象的每個類型而言的,而不是所有類型。 你要被動地寫 Dispose 和析構函數。對象的回收順序可以是任意的。你可能會碰到在對象被回收之前,它的成員變量之一已經被回收了的情況。你可能還沒發現這個問題因為 Dispose() 方法可被執行多次。如果對象已經被回收被再次調用,就什么也不做。析構函數的規則也一樣。任何對象的引用還在內存,就不用檢查 null 引用。然而任何你引用的對象都可能被回收了。或者已經被析構了。 你還會主要到 MyResourceHog 和 DerivedResourceHog 都沒有包含析構函數。這個例子的代碼沒有直接包含任何非托管資源。所以,析構函數是不需要的。這意味著這里代碼不需要調用 Dispose(false) 。這是就是正確的模式。除非你的類直接包含了非托管資源,否則你不需要析構函數。只有那些直接包含非托管資源的類才需要實現析構函數并且增加開銷。即使沒有被調用,析構函數的出現都會引入相當大的性能損失。除非你的類型需要析構函數,否則就不要添加它。然后,你仍要正確實現這個模式以保證只要子類添加非托管資源,就要添加析構函數,并且實現 Dispose(bool) 能正確處理非托管資源。 這帶給我最重要的建議就是任何涉及回收或清理的方法:你都應該只是釋放資源。不要在回收清理方法做其他操作。因為在 Dispose 和析構函數中處理其他操作會引入嚴重的問題。對象由構造函數而生,由垃圾回收器回收而滅亡。當你程序沒有訪問它們,你可以把它們當做休眠狀態。如果你不能訪問對象,就不能調用它的方法。對于這樣的意圖和目的,它就是死的。但是有析構函數的對象在它們被宣告死亡之前還能喘最后一口氣。析構函數應該只是清理非托管資源。如果在析構函數訪問這個對象,那么這個對象又會復活了。它還是活的但是不好,即使從休眠狀態喚醒了。下面就是很明顯的例子: ``` public class BadClass { // Store a reference to a global object: private static readonly List<BadClass> finalizedList = new List<BadClass>(); private string msg; public BadClass(string msg) { // cache the reference: msg = (string)msg.Clone(); } ~BadClass() { // Add this object to the list. // This object is reachable, no // longer garbage. It's Back! finalizedList.Add(this); } } ``` 當 BadClass 對象執行析構函數式,它把自己的引用添加到隊列里去。這又使得它自己可被訪問。也就是它還活著!你所引入的對象問題是任何人都畏縮的。對象一旦析構了,垃圾回收器就會相信它不再需要調用析構函數。如果你實際想在析構函數中復活一個對象,這是不會發生的。第二,你的一些資源可能不能再被訪問。GC 將不會從內存移除任何只有析構隊列能訪問的對象。但是它可能已經被析構過了。如果是這樣,它們絕大多數不能再被使用。盡管 BadClass 擁有的成員還是停留在內存,它們看起來很像被回收或析構了。你沒有任何方法能控制析構的順序。你不能使這種結構的工作可靠。情不要嘗試。 除了學院練習,我從來沒有看過像這樣復活對象的代碼。但我見過析構函數試圖做這件事調用函數保持引用帶回到有生命狀態。原則上析構函數上的任何代碼都要非常小心,推而廣之, Dispose 方法也一樣。如果代碼除了釋放資源之外還做了其他任何事情,請再次確認。這次操作很可能在將來發生問題。移除這些操作,并且保證析構函數和 Dispose() 函數只是釋放資源。 在托管的環境,你不需要為每個類都寫一個析構函數;只有存儲了非托管類型或者包含實現 IDisposable 的成員變量的類才需要。即使你只是需要 Disposable 接口,而不需要析構函數,但實現整個模式。否則,你限制你的子類以至于它們實現標準 Dispose 模式會很復雜。遵循我描述的標準 Dispose 模式習慣。那對于你,你類的使用者,繼承你類的人都會是更簡單的。 小結: 這篇介紹實現 Dispose 接口的準則,對于有引用非托管資源的就要實現 Dispose 接口并且寫析構函數,具體的實現模式文中有詳實的介紹。 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/2078979](/blog/2078979) 更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看