C# 托管资源和非托管资源(Dispose、析构函数)

https://www.cnblogs.com/herenzhiming/articles/9691524.html

资源分类:

托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收。

   非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。       

析构函数、Dispose函数:

在.NET中,Object.Finalize()方法是无法重载的,Object.Finalize()可以释放非托管资源(编译器是根据类的析构函数来自动生成),不能在析构函数中释放托管资源。 因为析构函数是有垃圾回收器调用的,可能在析构函数调用之前,类包含的托管资源已经被回收了,从而导致无法预知的结果。

本来如果按照上面做法,非托管资源也能够由垃圾回收器进行回收,但是非托管资源一般是有限的,比较宝贵的,而垃圾回收器是由CRL自动调用的,这样就无法保证及时的释放掉非托管资源,因此定义了一个Dispose()方法,让使用者能够手动的释放非托管资源。Dispose()方法释放类的托管资源和非托管资源,使用者手动调用此方法后,垃圾回收器不会对此类实例再次进行回收。Dispose()方法是由使用者调用的,在调用时,类的托管资源和非托管资源肯定都未被回收,所以可以同时回收两种资源。

Microsoft为非托管资源的回收专门定义了一个接口:IDisposable,接口中只包含一个Dispose()方法。任何包含非托管资源的类,都应该继承此接口。在一个包含非托管资源的类中,关于资源释放的标准做法是:

(1) 继承IDisposable接口;

(2) 实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);

(3) 实现类析构函数,在其中释放非托管资源。

如果类实现了IDisposable接口,实例化类时可以使用using关键字,则当超出using关键字作用域时会隐式调用Dispose函数。

using(CTest   inst   =   new   CTest())      
{                 
//Do   Something;         
}  

显示调用Dispose()方法:

可以及时的释放资源,同时通过移除Finalize()方法的执行,提高了性能;如果没有显示调用Dispose()方法,垃圾回收器也可以通过析构函数来释放非托管资源,垃圾回收器本身就具有回收托管资源的功能,从而保证资源的正常释放,只不过由垃圾回收器回收会导致非托管资源的未及时释放的浪费。

垃圾处理器调用析构:

在.NET中应该尽可能的少用析构函数释放资源。在没有析构函数的对象在垃圾处理器一次处理中从内存删除,但有析构函数的对象,需要两次,第一次调用析构函数,第二次删除对象。而且在析构函数中包含大量的释放资源代码,会降低垃圾回收器的工作效率,影响性能。所以对于包含非托管资源的对象,最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器。

下面是MSDN对这两个函数的建议使用方法:

//MSDN建议// Design pattern for a base class.public class Base : IDisposable{//保证重复释放资源时系统异常private bool _isDisposed = false;// 析构函数,编译器自动生成Finalize()函数由GC自动调用,保证资源被回收。// 最好不要声明空析构函数,造成性能问题// 如果没有引用非托管资源就不需要显示声明析构函数,会造成性能问题,系统会自动生成默认析构函数~Base(){// 此处只需要释放非托管代码即可,因为GC调用时该对象资源可能还不需要释放Dispose(false);}//外部手动调用或者在using中自动调用,同时释放托管资源和非托管资源public void Dispose(){Dispose(true);GC.SuppressFinalize(this); ///告诉GC不需要再次调用}protected virtual void Dispose(bool disposing){if (!_isDisposed){if (disposing){//释放托管资源}// 释放非托管资源// 释放大对象this._isDisposed = true;}}}

下面是通过Reflector工具对上面代码反射出来的结果,可以看出析构函数直接被翻译成Finalize()函数了,因为Finalize函数不能被重写,所以只能用析构函数的方式实现Finalize方法:

public class Base : IDisposable
{// Fieldsprivate bool _isDisposed;// Methodspublic Base();public void Dispose();protected virtual void Dispose(bool disposing);protected override void Finalize(); 
}

C#中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。

演示代码如下:

 1 using System;2 3 namespace ConsoleApplication4 {5     abstract class MyStream : IDisposable6     {7         public MyStream()8         {9             m_unmanagedResource = Marshal.AllocCoTaskMem(100);
10             m_bitmap = new Bitmap(50, 50);
11         }
12 
13         #region IDisposable Members
14         void IDisposable.Dispose()
15         {
16             Dispose(true);
17             GC.SuppressFinalize(this);
18         }
19 
20         protected virtual void Dispose(bool isDisposing)
21         {
22             if (!m_disposed)
23             {
24                 if (isDisposing)
25                 {
26                     m_bitmap.Dispose();
27                 }
28                 Marshal.FreeCoTaskMem(m_unmanagedResource);
29                 m_disposed = true;
30             }
31         }
32         
33         public void Close()
34         {
35             ((IDisposable)this).Dispose();
36         }
37 
38         ~MyStream()
39         {
40             Dispose(false);            
41         }
42 
43         private IntPtr m_unmanagedResource;     // Unmanaged resource
44         private Bitmap m_bitmap;                // IDisposable managed resources
45         private bool m_disposed;
46         
47         #endregion
48     }
49 
50     class MyDerivedStream : MyStream
51     {
52         public MyDerivedStream()
53         {
54             m_anotherMemory = Marshal.AllocCoTaskMem(20);
55             m_anotherImage = new Bitmap(24, 24);
56         }
57 
58         protected override void Dispose(bool isDisposing)
59         {
60             if (!m_disposed)
61             {
62                 if (isDisposing)
63                 {
64                     m_anotherImage.Dispose();
65                 }
66 
67                 Marshal.FreeCoTaskMem(m_anotherMemory);
68                 base.Dispose(isDisposing);
69                 m_disposed = true;
70             }
71         }
72 
73         public static void Main(string[] args)
74         {
75             MyStream aStream = new MyDerivedStream();
76 
77             aStream.Close();          // Allowed
78             // aStream.Dispose();    // Cannot compile
79 
80             ((IDisposable)aStream).Dispose();     // Allowed
81 
82             //
83             //  This one works as well, because newStream calls the explicit implemented
84             //  IDisposable.Dispose method
85             //
86             using (MyStream newStream = new MyDerivedStream())
87             {
88                 //
89                 //  Do something
90                 //
91             }
92         }
93         
94         private IntPtr m_anotherMemory;
95         private Bitmap m_anotherImage;
96         private bool m_disposed;
97     }
98 }
99 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shell 脚本知识回顾 (五) —— Shell 循环

一、Shell for循环 与其他编程语言类似,Shell支持for循环。 for循环一般格式为:for 变量 in 列表 docommand1command2...commandN done 列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次&…

android开发工具下载

android studio eclipse sdk adt

Shell 脚本知识回顾 (四) —— Shell 命令及Shell 相关语句

一、Shell echo命令 echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式:echo arg您可以使用echo实现更复杂的输出格式控制。 显示转义字符 echo "\"It is a test\""结果将是:"It is a test"…

qt工程。。。。。。

分享Qt多工程多目录的编译案例,subdirs Qt编译debug和release版本–CONFIG(debug,debug|release) QT工程pro设置实践(with QtCreator)----非弄的像VS同样才顺手? Qt创建动态库并添加动态库版本号 qmake:变量手册 QtCreator按顺序编译多个子项目

Shell 脚本知识回顾 (三) —— 替换、运算符、字符串、数组

一、Shell替换:Shell变量替换,命令替换,转义字符 如果表达式中包含特殊字符,Shell 将会进行替换。例如,在双引号中使用变量就是一种替换,转义字符也是一种替换。 举个例子: [cpp] view plaincop…

最幸福的事就是吃饺子

中午了,不知道吃什么,就去煮了点饺子,人呼呼的,吃完了很暖和~~下午出去,晚上回来,一天就这样过了~~转载于:https://blog.51cto.com/tina1314luky/1343466

Shell 脚本知识回顾 (二) —— Shell变量

一、Shell变量:Shell变量的定义、删除变量、只读变量、变量类型 Shell支持自定义变量。定义变量 定义变量时,变量名不加美元符号($),如: [cpp] view plaincopy variableName"value" 注意&…

Shell 脚本知识回顾 (一) —— 基础篇

一、Shell简介:什么是Shell,Shell命令的两种执行方式 Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的。Shell既是一种命令语言,又是一种程序设计语言。作为命令…

c实现面向对象编程(3)

http://blog.csdn.net/kennyrose/article/details/7564105

C 与 JAVA 的对比分析

Sun 公司推出的Java 是面向对象程序设计语言,其适用于Internet 应用的开发,称为网络时代重要的语言之一。Java 可以用认为是C 的衍生语言,与C 在大量元以内成分保持相同,例如此法结构、表达式语句、运算符等与C基本一致&#xff1…

红帽集群RHCS

1、简介:RHCS是RedHatClusterSuite的缩写,也就是红帽子集群套件,RHCS是一个能够提供高可用性、高可靠性、负载均衡、存储共享且经济廉价的集群工具集合,它将集群系统中三大集群架构融合一体,可以给web应用、数据库应用…

Java 基础——类的加载

当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三个步骤来对该类进行初始化,JVM将会连续完成这三个步骤,也把这三个步骤统称为类加载或类初始化; 类加载指的是…

HDUOJ-----1556Color the ball

Color the ball Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 6787 Accepted Submission(s): 3549 Problem DescriptionN 个气球排成一排&#xff0c;从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <…

Java 基础——数组解析

数组对于每一门编辑应语言来说都是重要的数据结构之一&#xff0c;当然不同语言对数组的实现及处理也不尽相同。 Java语言中提供的数组是用来存储固定大小的同类型元素。 可以声明一个数组变量&#xff0c;如numbers[100]来代替直接声明100个独立变量number0&#xff0c;number…

《在你身边,为你设计》-哪位知道下载、在线阅读地址啊?

《在你身边&#xff0c;为你设计》-前端UI必读出自腾讯CDChttp://cdc.tencent.com/?p6761今天听同事说这本书写的非常好&#xff0c;改变了他关于前端UI的许多看法&#xff0c;可谓&#xff1a;醍醐灌顶。可惜我网上找了下都需要Money买&#xff0c;哪位有在线阅读、PDF下载地…

Java 基础——类和对象

Java作为一种面向对象语言。支持以下基本概念&#xff1a; •多态 •继承 •封装 •抽象 •类 •对象 •实例 •方法 •消息解析 本节我们重点研究类与对象的概念。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff0c…

报告显示Q2 Android平板电脑全球市场份额达67%

网易科技讯 7月30日消息&#xff0c;据国外媒体报道&#xff0c;市场调研公司Strategy Analytics最新公布的报告显示&#xff0c;2013年第二季度平板电脑全球出货量达到5170万台&#xff0c;比去年同期的3610万台增长43&#xff05;&#xff0c;其中Android、iOS和Windows平板电…

活期储蓄账目管理系统

基本要求&#xff1a;实现储户开户、销户、存入、支出等活动。要求能查找储户的账户&#xff0c;实现存款、取款、插入、删除等操作。具体功能如下&#xff1a;&#xff08;1&#xff09;实现储户开户。&#xff08;2&#xff09;实现储户销户。&#xff08;3&#xff09;向某账…

面向过程 VS 面向对象

面向过程&#xff08;Process Oriented&#xff09;这个词是在面向对象&#xff08;Object Oriented&#xff09;出现之后为与之相对而提出的。其实它在以前基本被叫做“结构化编程”。 早期的程序设计&#xff0c;大量使用共享变量&#xff08;全局变量&#xff09;和GOTO语句…

一、OpenStack架构

DashBoardHorizon提供WEB界面ComputerNova计算也就是虚拟机NetworkingNeutron提供给nova网络支持Object StorageSwift提供对象存储Block StorageCinder提供云硬盘给nova&#xff0c;同时备份到SwiftIdentity SserviceKeystone提供所有组件的认证Image ServiceGlance提供给nova镜…