• 正确实现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
    //
    }
    }

    }
    }
  • 相关阅读:
    bow lsa plsa
    拉普拉斯平滑处理 Laplace Smoothing
    程序员的出路在哪里
    Android各代码层获取系统时间的方法
    TCP/IP笔记 二.网络层(2)——ICMP,RIP,OSPF,BGP
    利用Xtrabackup备份集合恢复一台从库的过程
    char *和char[]的区别,困扰很长时间了,总结下
    不使用webview,用手机浏览器的android app
    [置顶] oracle 快速查询数据库各种信息、及转换对应java代码
    MySQL备份方案-->(利用mysqldump以及binlog二进制日志)
  • 原文地址:https://www.cnblogs.com/Athrun/p/IDisposable_Dispose_Close.html
Copyright © 2020-2023  润新知