簡體   English   中英

編寫IDisposable的實際示例

[英]Writing a practical example of IDisposable

我一直在閱讀有關Dispose模式的信息,我有點理解它的用途(清理資源,以便我的應用程序不會泄漏內存),但是我想在一個實際示例中看到它。

我的想法是編寫一個簡單的應用程序,該應用程序首先使用一些資源而不處理它們,然后在對代碼進行一些更改之后適當地處理這些資源。 我想看到的是代碼更改之前/之后的內存使用情況,以可視化處理方式。

問題:我可以使用哪些對象? 我試圖用一些大圖像(大小超過15 MB的JPEG圖像)來做,但是我無法從中建立一個實際的例子。 當然,我願意接受其他想法。

該單元測試可以完成您要描述的內容。 無論是否調用Dispose ,它都執行相同的操作,以顯示不調用Dispose時發生的情況。

這兩種方法都會創建一個文件,打開文件,寫入文件,然后再次打開並再次寫入文件。

第一個方法拋出IOException因為沒有處理StreamWriter 該文件已打開,無法再次打開。

第二個在嘗試重新打開文件之前進行了處理,並且工作正常。

[TestClass]
public class DisposableTests
{
    [TestMethod]
    [ExpectedException(typeof(IOException))]
    public void DoesntDisposeStreamWriter()
    {
        var filename = CreateFile();
        var fs = new StreamWriter(filename);
        fs.WriteLine("World");
        var fs2 = new StreamWriter(filename);
        fs2.WriteLine("Doesn't work - the file is already opened.");
    }

    [TestMethod]
    public void DisposesStreamWriter()
    {
        var filename = CreateFile();
        var fs = new StreamWriter(filename);
        fs.WriteLine("World");
        fs.Dispose();
        var fs2 = new StreamWriter(filename);
        fs2.WriteLine("This works");
        fs2.Dispose();
    }

    private string CreateFile()
    {
        var filename = Guid.NewGuid() + ".txt";
        using (var fs = new StreamWriter(filename))
        {
            fs.WriteLine("Hello");
        }
        return filename;
    }
}

您可能不會看到內存使用問題,因為這不是IDisposable專門解決的問題。 所有對象都使用內存,但大多數不是一次性的。 垃圾回收器從不再引用的對象中回收內存(例如在方法中創建但在方法結束時超出范圍的對象)。

IDisposable適用於類保留某些未進行垃圾收集的資源的情況。 另一個示例是SqlConnection 這與內存無關,與與SQL Server的連接有關。 只有這么多可用。 (它最終會被發布,但不是以可預見的方式發布的-這是題外話。)

類可能是IDisposable的確切原因各不相同。 他們通常沒有共同之處。 IDisposable不在乎原因是什么。 這只是意味着該類中的某些東西需要清理。

以下示例顯示了實現IDisposable接口的一般最佳實踐。 創建一些MyResouce實例並在不調用Dispose方法的情況下對其進行測試,這會導致內存泄漏。 然后,在完成MyResouce實例后,每次都調用Dispose,不會發生內存泄漏。 參考

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM