100165346.htm

来自「C#高级编程(第三版),顶死你们。。 。up」· HTM 代码 · 共 173 行 · 第 1/3 页

HTM
173
字号
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; ~MyClass()&nbsp; </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // implementation</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">编译器在编译析构函数时,会隐式地把析构函数的代码编译为</span><span lang="EN-US">Finalize()</span><span style="FONT-FAMILY: 宋体">方法的对应代码,确保执行父类的</span><span lang="EN-US">Finalize()</span><span style="FONT-FAMILY: 宋体">方法。下面列出了编译器为</span><span lang="EN-US">~MyClass()</span><span style="FONT-FAMILY: 宋体">析构函数生成的</span><span lang="EN-US">IL</span><span style="FONT-FAMILY: 宋体">的对应</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">代码:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">protected override void Finalize()</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; try</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // implementation</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; finally</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base. Finalize();&nbsp;&nbsp; </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 32.25pt"><span lang="EN-US">}</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">如上所示,在</span><span lang="EN-US">~MyClass()</span><span style="FONT-FAMILY: 宋体">析构函数中执行的代码封装在</span><span lang="EN-US">Finalize()</span><span style="FONT-FAMILY: 宋体">方法的一个</span><span lang="EN-US">try</span><span style="FONT-FAMILY: 宋体">块中。对<span style="LETTER-SPACING: 0.2pt">父类</span></span><span lang="EN-US" style="LETTER-SPACING: 0.2pt">Finalize()</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.2pt">方法的调用放在</span><span lang="EN-US" style="LETTER-SPACING: 0.2pt">finally</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.2pt">块中,确保该调用的执行。第</span><span lang="EN-US" style="LETTER-SPACING: 0.2pt">11</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.2pt">章会讨论</span><span lang="EN-US" style="LETTER-SPACING: 0.2pt">try</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.2pt">块和</span><span lang="EN-US" style="LETTER-SPACING: 0.2pt">finally</span><span style="FONT-FAMILY: 宋体">块。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">有经验的</span><span lang="EN-US">C++</span><span style="FONT-FAMILY: 宋体">开发人员扩展了析构函数的用法,有时不仅用于清理资源,还提供调试信息或执行其他任务。</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">析构函数的使用要比在</span><span lang="EN-US">C++</span><span style="FONT-FAMILY: 宋体">中少得多,与</span><span lang="EN-US">C++</span><span style="FONT-FAMILY: 宋体">析构函数相比,</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">析构函数的问题是它们的不确定性。在删除</span><span lang="EN-US">C++</span><span style="FONT-FAMILY: 宋体">对象时,其析构函数会立即运行。但由于垃圾收集器的工作方式,无法确定</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">对象的析构函数何时执行。所以,不能在析构函数中放置需要在某一时刻运行的代码,也不应使用能以任意顺序对不同类实例调用的析构函数。如果对象<span style="LETTER-SPACING: 0.1pt">占用了宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放</span>了。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">另一个问题是</span><span lang="EN-US" style="LETTER-SPACING: 0.1pt">C#</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: 0.1pt">析</span><span style="FONT-FAMILY: 宋体">构函数的执行<span style="LETTER-SPACING: 0.1pt">会延迟对象最终从内存中删除的时间。没有析构函数的对象会在垃圾收集器的一次处理中从内存中删除,但有析构函数的对象需要两次处理才能删除:第一次调用析构函数时,没有删除对象,第二次调用才真正删除对象。</span>另外,运行库使用一个线程来执行所有对象的</span><span lang="EN-US">Finalize()</span><span style="FONT-FAMILY: 宋体">方法。如果频繁使用析构函数,而且使用它们执行长时间的清理任务,对性能的影响就会非常显著。</span></p>
<h3 style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 0cm; MARGIN-RIGHT: 0cm; FTEL: 8.15pt"><a ftel="Close"></a><a ftel="Dispose"></a><a ftel="IDisposable"></a><a ftel="_Toc507815014"><span lang="EN-US">7.2.2&nbsp; IDisposable</span></a><span style="FONT-FAMILY: 黑体">接口</span></h3>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">一个推荐替代析构函数的方式是使用</span><span lang="EN-US">System.IDisposable</span><span style="FONT-FAMILY: 宋体">接口。</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">接口定义了一个模式</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">具有语言级的支持</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体">,为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾函数器相关的问题。</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">接口声明了一个方法</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">,它不带参数,返回</span><span lang="EN-US">void</span><span style="FONT-FAMILY: 宋体">,</span><span lang="EN-US">Myclass</span><span style="FONT-FAMILY: 宋体">的方法</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">的执行代码如下:</span></p>
<p class="2" style="MARGIN-TOP: 4.9pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">class Myclass : IDisposable</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; LINE-HEIGHT: 13pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; LINE-HEIGHT: 13pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; public void Dispose() </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; LINE-HEIGHT: 13pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // implementation</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; LINE-HEIGHT: 13pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">的执行代码显式释放由对象直接使用的所有未托管资源,并在所有实现</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">接口的封装对象上调用</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">。这样,</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">方法在释放未托管资源时提供了精确的控制。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">假定有一个类</span><span lang="EN-US">ResourceGobbler</span><span style="FONT-FAMILY: 宋体">,它使用某些外部资源,且执行</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">接口。如果要实例化这个类的实例,使用它,然后释放它,就可以使用下面的代码:</span><span lang="EN-US"> </span></p>
<p class="2" style="MARGIN-TOP: 4.9pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">ResourceGobbler theInstance = new ResourceGobbler();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; // do your processing </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp; </span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">theInstance.Dispose();</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">如果在处理过程中出现异常,这段代码就没有释放</span><span lang="EN-US">theInstance</span><span style="FONT-FAMILY: 宋体">使用的资源,所以应使用</span><span lang="EN-US">try</span><span style="FONT-FAMILY: 宋体">块</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">详见第</span><span lang="EN-US">11</span><span style="FONT-FAMILY: 宋体">章</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体">,编写下面的代码:</span></p>
<p class="2" style="MARGIN-TOP: 4.9pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">ResourceGobbler theInstance = null;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">try</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; theInstance = new ResourceGobbler();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 32.25pt"><span lang="EN-US">// do your processing </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">finally&nbsp; </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp; if (theInstance != null) theInstance.Dispose();</span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">即使在处理过程中出现了异常,这个版本也可以确保总是在</span><span lang="EN-US">theInstance</span><span style="FONT-FAMILY: 宋体">上调用</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">,总是释放由</span><span lang="EN-US">theInstance</span><span style="FONT-FAMILY: 宋体">使用的资源。但是,如果总是要重复这样的结构,代码就很容易被混淆。</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">提供了一种语法,可以确保在引用超出作用域时,在对象上自动调用</span><span lang="EN-US">Dispose()(</span><span style="FONT-FAMILY: 宋体">但不是</span><span lang="EN-US">Close())</span><span style="FONT-FAMILY: 宋体">。该语法使用了</span><span lang="EN-US">using</span><span style="FONT-FAMILY: 宋体">关键字来完成这一工作<span style="LETTER-SPACING: -1pt">&mdash;&mdash;</span></span><span style="LETTER-SPACING: -1pt"> </span><span style="FONT-FAMILY: 宋体">但目前,在完全不同的环境下,它与命名空间没有关系。下面的代码生成与</span><span lang="EN-US">try</span><span style="FONT-FAMILY: 宋体">块相对应的</span><span lang="EN-US">IL</span><span style="FONT-FAMILY: 宋体">代码:</span></p>
<p class="2" style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">using (ResourceGobbler theInstance = new ResourceGobbler())</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; FTEL: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; // do your processing </span></p>
<p class="2" style="MARGIN-TOP: 0cm; MARGIN-LEFT: 21.45pt; MARGIN-RIGHT: 0cm; FTEL: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span lang="EN-US">using</span><span style="FONT-FAMILY: 宋体">语句的后面是一对圆括号,其中是引用变量的声明和实例化,该语句使变量放在随附的复合语句中。另外,在变量超出作用域时,即使出现异常,也会自动调用其</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">方法。如果已经使用</span><span lang="EN-US">try</span><span style="FONT-FAMILY: 宋体">块来捕获其他异常,就会比较清晰,如果避免使用</span><span lang="EN-US">using</span><span style="FONT-FAMILY: 宋体">语句,仅在已有的</span><span lang="EN-US">try</span><span style="FONT-FAMILY: 宋体">块的</span><span lang="EN-US">finally</span><span style="FONT-FAMILY: 宋体">子句中调用</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">,还可以避免进行额外的缩进。</span></p>
<p class="a3" style="MARGIN-TOP: 8.15pt; FTEL: 21.45pt"><span style="FONT-FAMILY: 黑体">注意:</span></p>
<p class="a1" style="FTEL: 21.45pt"><span style="FONT-FAMILY: 楷体_GB2312">对于某些类来说,使用</span><span lang="EN-US">Close()</span><span style="FONT-FAMILY: 楷体_GB2312">要比</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 楷体_GB2312">更富有逻辑性,例如,在处理文件或数据库连接时,就是这样。在这些情况下,常常实现</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 楷体_GB2312">接口,再执行一个独立的</span><span lang="EN-US">Close()</span><span style="FONT-FAMILY: 楷体_GB2312">方法,来调用</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 楷体_GB2312">。这种方法在类的使用上比较清晰,还支持</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 楷体_GB2312">提供的</span><span lang="EN-US">using</span><span style="FONT-FAMILY: 楷体_GB2312">语句。</span></p>
<h3 style="MARGIN-TOP: 8.15pt; MARGIN-LEFT: 0cm; MARGIN-RIGHT: 0cm; FTEL: 8.15pt"><a ftel="_Toc507815015"><span lang="EN-US">7.2.3&nbsp; </span></a><span style="FONT-FAMILY: 黑体">实现</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 黑体">接口和析构函数</span></h3>
<p class="MsoNormal" style="LINE-HEIGHT: 16pt"><a ftel="Finalize"></a><a ftel="garbage"><span style="FONT-FAMILY: 宋体">前面的章节讨论了类所使用的释放未托管资源的两种方式:</span></a></p>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?