zoukankan      html  css  js  c++  java
  • 正确实现IDisposable Dispose和Close的区别

    正確實現IDisposable Dispose方法

    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace NET.MST.Third.FinalizeDispose
    {
    public class FinalizeDisposeBase : IDisposable
    {
    // 标记对象是否已被释放
    private bool _disposed = false;

    // Finalize方法:析構函數
    ~FinalizeDisposeBase()
    {
    Dispose(
    false);
    }

    // 这里实现了IDispose中的 Dispose方法
    public void Dispose()
    {
    Dispose(
    true);
    //告诉GC此对象的Finalize方法不再需要调用
    GC.SuppressFinalize(true);
    }

    //在这里做实际的析构工作
    //申明为虚方法以供子类在有必要时重写
    protected virtual void Dispose(bool isDisposing)
    {
    // 当对象已经被析构时,不再执行
    if (_disposed)
    return;
    if (isDisposing)
    {
    //在这里释放托管资源
    //只在用户调用Dispose方法时执行
    }
    //在这里释放非托管资源

    //标记对象已被释放
    _disposed = true;
    }
    }

    public sealed class FinalizeDispose : FinalizeDisposeBase
    {
    private bool _mydisposed = false;

    protected override void Dispose(bool isDisposing)
    {
    // Don't dispose more than once.
    if (_mydisposed)
    return;
    if (isDisposing)
    {
    //在这里释放托管的并且在这个类型中申明的资源
    }
    //在这里释放非托管的并且在这个类型中申明的资源

    //调用父类的Dispose方法来释放父类中的资源
    base.Dispose(isDisposing);

    // 设置子类的标记
    _mydisposed = true;
    }

    static void Main()
    {
    }
    }
    }

    在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize。Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的。

    在这个模式中,void Dispose(bool disposing)函数通过一个disposing参数来区别当前是被Dispose()调用还是被析构函数调用(当disposing为“true”时,说明Dispose()是被程序显示调用的,需要释放托管资源和非托管资源;当disposing为“false”时,说明Dispose()是被析构函数(也就是C#的Finalize())调用的,只需要释放非托管资源)。

    这是因为,Dispose()函数是被其它代码在程序中显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候MyResource所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。

    然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。

    因此,上面的模式保证了:
    1、 Finalize只释放非托管资源;
    2、 Dispose释放托管和非托管资源;
    3、 重复调用Finalize和Dispose是没有问题的;
    4、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。

    在C#中,这个模式需要显式地实现,其中C#的析构函数~MyResource()代表了Finalize()。

    关于资源释放,最后一点需要提的是Close函数。在语义上它和Dispose很类似,按照MSDN的说法,提供这个函数是为了让用户感觉舒服一点,因为对于某些对象,例如文件,用户更加习惯调用Close()。

    那么Dispose和Close到底有什么区别?
    当我们开发C#代码的时候,经常碰到一个问题,有些class提供Close(),有些class提供Dispose(),那么Dispose和Close到底有什么区别?

    首先,Dispose和Close基本上应该是一样的。Close是为了那些不熟悉Dispose的开发者设计的。因为基本上所有的developer都知道Close是干吗的(特别是对于那些有C++背景的developer)。

    但是当我们写code时候,如果要实现Close和Dispose的时候,要注意Close和Dispose的设计模式。.net的一些class只提供Close,而且派生自IDisposable,并且隐藏了Dispose方法。是不是觉得很不明白了?

    对这些class来说,关键在于它们显式的(explicitly)实现了IDisposable。对于隐式实现来说,你只需要调用"new A().Dispose()",但是对于显式实现来说,Dispose不会是这个class的成员函数。唯一的调用方式是你先要cast到 IDisposable才行。(“new A().Dispose()”编译不过,但是“((IDisposable)new A()).Dispose()”可以编译过)。所以这样就符合了设计的要求:提供Close(),隐藏Dispose(),并且实现了 IDisposable接口。

    在.net的framework里,Close()被设计成public的,并且在Close()里面call被隐藏的Dispose(); Dispose()去call另一个virtual的Dispose(bool)函数。所以如果你从这个class继承,你就必须实现Dispose (bool)方法。

    调用者call Close()的时候就会call到你重载的那个Dispose(bool)方法去释放资源。

    请参考 http://blogs.msdn.com/brada/archive/2003/07/06/50127.aspx

    注意事项:
    1,Close()不应该被定义成virtual。对于这个design pattern,Close()只是用来call那个隐藏的Dispose(),用户不应该改变Close的behavior。对于这个问题, System.IO.Stream也有设计问题。之所以有问题是为了满足向后兼容的需求。See http://msdn2.microsoft.com/en-us/library/ms227422.aspx. 文档里面提到虽然Close()是virtual的,但是不应该被override。

    示例代碼:

    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Drawing;
    using System.Drawing.Imaging;

    namespace ConsoleApplication
    {
    abstract class MyStream : IDisposable
    {
    //私有字段
    private IntPtr m_unmanagedResource; // Unmanaged resource
    private Bitmap m_bitmap; // IDisposable managed resources
    private bool m_disposed;
    //構造函數
    public MyStream()
    {
    m_unmanagedResource
    = Marshal.AllocCoTaskMem(100);
    m_bitmap
    = new Bitmap(50, 50);
    }
    //析構函數,代表Finalize()
    ~MyStream()
    {
    Dispose(
    false);
    }
    // 这里实现了IDispose中的 Dispose方法
    void IDisposable.Dispose()
    {
    Dispose(
    true);
    //告诉GC此对象的Finalize方法不再需要调用
    GC.SuppressFinalize(true);
    }
    //在这里做实际的析构工作
    //申明为虚方法以供子类在有必要时重写
    protected virtual void Dispose(bool isDisposing)
    {
    // 当对象已经被析构时,不再执行
    if (!m_disposed)
    {
    if (isDisposing)
    {
    //在这里释放托管资源
    //只在用户调用Dispose方法时执行
    m_bitmap.Dispose();
    }
    //在这里释放非托管资源
    Marshal.FreeCoTaskMem(m_unmanagedResource);
    //标记对象已被释放
    m_disposed = true;
    }
    }

    //显式的(explicitly)实现了IDisposable
    public void Close()
    {
    ((IDisposable)
    this).Dispose();
    }
    }

    /// <summary>
    /// 子類,繼承自MyStream
    /// </summary>
    class MyDerivedStream : MyStream
    {
    // 私有字段
    private IntPtr m_anotherMemory;
    private Bitmap m_anotherImage;
    private bool m_disposed;

    //構造函數
    public MyDerivedStream()
    {
    m_anotherMemory
    = Marshal.AllocCoTaskMem(20);
    m_anotherImage
    = new Bitmap(24, 24);
    }
    //重載Dispose方法
    protected override void Dispose(bool isDisposing)
    {
    if (!m_disposed)
    {
    if (isDisposing)
    {
    //在这里释放托管的并且在这个类型中申明的资源
    m_anotherImage.Dispose();
    }
    //在这里释放非托管的并且在这个类型中申明的资源
    Marshal.FreeCoTaskMem(m_anotherMemory);
    //调用父类的Dispose方法来释放父类中的资源
    base.Dispose(isDisposing);
    m_disposed
    = true;
    }
    }

    public static void Main(string[] args)
    {
    MyStream aStream
    = new MyDerivedStream();

    aStream.Close();
    // Allowed
    // aStream.Dispose(); // Cannot compile

    ((IDisposable)aStream).Dispose();
    // Allowed

    //
    // This one works as well, because newStream calls the explicit implemented
    // IDisposable.Dispose method
    //
    using (MyStream newStream = new MyDerivedStream())
    {
    //
    // Do something
    //
    }
    }

    }
    }
  • 相关阅读:
    AcWing 1135. 新年好 图论 枚举
    uva 10196 将军 模拟
    LeetCode 120. 三角形最小路径和 dp
    LeetCode 350. 两个数组的交集 II 哈希
    LeetCode 174. 地下城游戏 dp
    LeetCode 面试题 16.11.. 跳水板 模拟
    LeetCode 112. 路径总和 递归 树的遍历
    AcWing 1129. 热浪 spfa
    Thymeleaf Javascript 取值
    Thymeleaf Javascript 取值
  • 原文地址:https://www.cnblogs.com/Athrun/p/IDisposable_Dispose_Close.html
Copyright © 2011-2022 走看看