确保try...finally...和using会执行完毕

标签: try finally using | 发表时间:2011-01-07 12:06 | 作者:PowerCoder Lee
出处:http://www.cnblogs.com/

说到c#中的try...finally...和using我想大多数人都不会陌生,这两个结构在C#中起着至关重要的作用,就是在程序抛出异常的时候仍然能够确保程序执行完某一部分代码,对于try...finally...就是在try块抛出异常时,确保仍然执行finally块中的代码,对于using就是在using块中的代码在抛出异常时,仍然执行在using上声明的对象的接口IDisposable.Dispose方法(后面会讲到这实际上还是通过try...finally...实现的)。

 

但是你确信你的try...finally...块在发生异常时一定会执行finally,你的using块发生异常时一定会执行Dispose方法吗?

 

我们首先来看看try...finally...,请先建立一个控制台项目,在粘贴如下代码:

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

namespace ExceptionTest
{
    
class Demo : IDisposable
    {

        
public void Dispose()
        {
            Console.WriteLine(
"Execute Dispose!");
        }
    }

    
class Program
    {
        
static void TryFinallyTest()
        {
            Demo demo 
= new Demo();

            
try
            {
                
throw new Exception();
            }
            
finally
            {
                demo.Dispose();
            }
        }

        
static void Main(string[] args)
        {
            TryFinallyTest();
        }
    }
}

 

这段代码很简单就是在try块发生异常后执行demo.Dispose()在控制台上输出一个字符串,但是请执行该代码,在抛出异常后点否(即不调试),结果你会发现控制台上什么都没有输出,而且程序的进程被终止了。

这说明这里的try...finally...块在发生异常后根本没有执行finally块的demo.Dispose(),这不是和前面所说的相背吗?

 

关闭控制台,我们再来执行一次上面的代码,这次在抛出异常时选择是,并且在VS进入调试状态后,选择停止调试。

结果我们发现控制台上显示抛出了异常,并且显示Execute Dispose!,很显然这次finally块的demo.Dispose()在抛出异常后执行了。

 

请注意你可以在windows的任务管理器中查看到这两种情况程序进程的存在状态有所差异,如果你在程序抛出异常后点击否,那么你会发现程序的进程就立即结束了,然而你在程序抛出异常后点击是,你会发现程序进程并没有立即结束,而是等到输出Execute Dispose!后进程才结束。这个现象很明确的表示了第一种情况没能执行finally块的原因就是程序在try中抛出异常后,还没来得及执行finally块,程序的进程立即就被操作系统终止了。同样的情况也会出现在windows项目中,请见最后的示例代码。

 

可见在控制台项目和windows项目中try块在抛出异常后如果没有相应的catch块来捕获异常,会导致程序进程立即终止,最终导致本来该执行的finally块的代码没被执行。那么为了让try...finally...的finally始终能被执行,那么我们就要想办法让try中的异常抛出后程序进程不要立即被终止,而是等到finally块被执行后再被终止,有一个很简单的办法可以实现这个需求,请看如下代码:

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

namespace ExceptionTest
{
    
class Demo : IDisposable
    {

        
public void Dispose()
        {
            Console.WriteLine(
"Execute Dispose!");
        }
    }

    
class Program
    {
        
static void InnerTryFinallyTest()
        {
            
try
            {
                Demo demo 
= new Demo();

                
try
                {
                    
throw new Exception();
                }
                
finally
                {
                    demo.Dispose();
                }
            }
            
catch
            {
                
throw;
            }
        }

        
static void Main(string[] args)
        {
            InnerTryFinallyTest();
        }
    }
}

在方法InnerTryFinallyTest中,我们将try...finally...结构放在了另一个try...catch...结构中,这样的嵌套结构起着一个很关键的作用,就是在try...finally...结构的try块中发生异常后,该异常不会立即报告给操作系统,而是先将该异常传递给外层的try...catch...结构的try块,外层的try块首先会保证其内部该执行的代码都执行完毕后再将异常传给catch块捕获(其中所谓的该执行的代码就是指内部try...finally...结构的finally块代码),最后catch块使用关键字throw将捕获的异常再原封不动的抛出给操作系统,此时程序进程立刻被操作系统终止。执行上述代码得到:

果然这次在异常抛出给操作系统前,先执行了finally块的代码。

 

之后我们再来看看using结构,c#中的using结构这里就不多作介绍了,不知道的朋友请查阅MSDN相关部分。你会在MSDN上查到using结构最后实际上会被编译器转换为try...finally...结构,假如有如下using结构:

using (Demo demo = new Demo())
{
    throw new Exception();
}

编译器编译后得到的实际上是如下结构:

Demo demo = new Demo();

try
{
    throw new Exception();
}
finally
{
    if (demo != null)
      ((IDisposable)demo).Dispose();
}

所以在控制台项目和windows项目中,出现在try...finally...结构上的问题,同样会出现在using结构上,请见如下代码:

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

namespace ExceptionTest
{
    
class Demo : IDisposable
    {

        
public void Dispose()
        {
            Console.WriteLine(
"Execute Dispose!");
        }
    }

    
class Program
    {
        
static void UsingTest()
        {
            
using (Demo demo = new Demo())
            {
                
throw new Exception();
            }
        }

        
static void Main(string[] args)
        {
            UsingTest();
        }
    }
}

同样控制台上不会输出Execute Dispose!,证明using结构抛出异常后,并没有执行IDisposable.Dispose()方法,原因就是using结构内部发生异常后程序进程就被立即终止了。同理在using结构外层加上try...catch...结构就可以避免using结构抛出异常后程序进程被立即终止:

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

namespace ExceptionTest
{
    
class Demo : IDisposable
    {

        
public void Dispose()
        {
            Console.WriteLine(
"Execute Dispose!");
        }
    }

    
class Program
    {
        
static void InnerUsingTest()
        {
            
try
            {
                
using (Demo demo = new Demo())
                {
                    
throw new Exception();
                }
            }
            
catch
            {
                
throw;
            }
        }

        
static void Main(string[] args)
        {
            InnerUsingTest();
        }
    }
}

这样就会在程序进程终止前执行IDisposable.Dispose()方法输出Execute Dispose!了。

 

上面都是对于控制台项目和windows项目展开的讨论,实际上导致上述问题的最终原因还是因为这两种项目将异常抛出给操作系统后,项目程序的进程会被操作系统终止。那么对于ASP.NET项目会不会有这个问题呢?大家都知道ASP.NET的代码都是由IIS的进程来负责执行,并且ASP.NET的代码发生了异常之后,会将异常信息输出在页面上,IIS的进程并不会被终止掉,那么是不是表示在ASP.NET中使用try...finally...结构和using结构不存在上述问题呢?我们新建一个ASP.NET web应用程序并且新建个类库文件输入如下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;

namespace WebAppException
{
    
class Demo : IDisposable
    {
        
public void Dispose()
        {
            
string path = HttpContext.Current.Server.MapPath("~/"+ "Log.txt";
            StreamWriter sw 
= new StreamWriter(path, true);
            sw.WriteLine(
"Execute Dispose!");
            sw.Close();
        }
    }

    
class EClass
    {
        
public static void TryFinallyTest()
        {
            Demo demo 
= new Demo();

            
try
            {
                
throw new Exception();
            }
            
finally
            {
                demo.Dispose();
            }
        }


        
public static void UsingTest()
        {
            
using (Demo demo = new Demo())
            {
                
throw new Exception();
            }
        }
    }
}

由于ASP.NET的页面在抛出异常后页面显示的将会是异常信息。所以这里我们将Dispose方法输出的内容改为输出到一个文本文件上(具体项目结构请见最后的示例程序下载),然后新建一个aspx页面调用上面EClass的两个测试方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebAppException
{
    
public partial class Default : System.Web.UI.Page
    {
        
protected void Page_Load(object sender, EventArgs e)
        {
            EClass.TryFinallyTest();
        }
    }
}

执行后发现Log.txt中出现了一行Execute Dispose!文字,这说明try...finally...结构在try块抛出异常后由于IIS进程未被终止还执行了finally块,同样执行EClass.UsingTest()会得到相同的结果。可见在ASP.NET中并不存在本文所述的问题。

 

由此可见try...finally...结构和using结构在不同的项目中表现的行为也有所不同,具体来说就是项目本身在抛出异常后其进程是否会被操作系统终止,这里我只试验了windows项目、控制台项目和ASP.NET,关于.NET中的其他项目如果大家遇到相同的问题就可以借鉴本文所描述内容(外层嵌套try...catch...结构)来处理。

 

最后是本文的示例代码(代码使用VS2010编写):

示例代码

作者: PowerCoder 发表于 2011-01-07 12:06 原文链接

评论: 12 查看评论 发表评论


最新新闻:
· 汉王开发9.7寸高分辨率电子书(2011-01-08 14:26)
· 甲骨文全球80个城市启动云计算产品路演(2011-01-08 12:00)
· 福特针对电动版福克斯推出手机应用(2011-01-08 11:59)
· 我国手机人口超8亿 手机成百姓开门第八件事(2011-01-08 11:55)
· CES:Copia电子阅读器助力Win7平板电脑(2011-01-08 11:30)

编辑推荐:网景联合创始人:从浏览器之父到点金之手

网站导航:博客园首页  我的园子  新闻  闪存  小组  博问  知识库

相关 [try finally using] 推荐:

确保try...finally...和using会执行完毕

- Lee - 博客园-首页原创精华区
但是你确信你的try...finally...块在发生异常时一定会执行finally,你的using块发生异常时一定会执行Dispose方法吗. 我们首先来看看try...finally...,请先建立一个控制台项目,在粘贴如下代码:. 这段代码很简单就是在try块发生异常后执行demo.Dispose()在控制台上输出一个字符串,但是请执行该代码,在抛出异常后点否(即不调试),结果你会发现控制台上什么都没有输出,而且程序的进程被终止了.

try、catch、finally巧遇return

- - CSDN博客编程语言推荐文章
         Java的基本理念是“结构不佳的代码不能运行”--Thinking in Java.          Java中的异常处理是一种在编译阶段的错误报告机制,是通过编译器强制执行的. 是:编译期间并不能找出所有的错误,余下的问题必须在运行期间解决.          简单的来说,异常处理目的在于通过少量的代码是你的程序更加可靠.

try-catch-finally中的4个大坑,不小心就栽进去了!

- - 掘金 后端
在 Java 语言中 try-catch-finally 看似简单,一副人畜无害的样子,但想要真正的“掌控”它,却并不是一件容易的事. 别的不说,咱就拿 fianlly 来说吧,别看它的功能单一,但使用起来却“暗藏杀机”,若您不信,咱来看下面的这几个例子.... 坑1:finally中使用return.

try! try! try!–在浏览器里试用NoSQL产品

- Leo Pay - NoSQLfan
通常要使用某个存储设备,我们可能需要经历下载、安装、配置、启动等多个过程,然后才能开始试验具体的试用. 而在我们很多 NoSQL 产品中,提供了一些非常方便的试用途径,可以让你不用安装一个自己的 Server 即可直接试用并学习其功能:. 1.try redis 在浏览器里试用Redis. 地址:http://try.redis-db.com/.

Deploy the spring cloud project using jenkins

- - Telami
先简单记录下Jenkins部署maven聚合工程要点. Root pom配置成项目根目录的pom.xml. maven命令单独install 欲构建的项目. 选项后可跟随{groupId}:{artifactId}或者所选模块的相对路径(多个模块以逗号分隔). 表示同时处理选定模块所依赖的模块. 表示同时处理依赖选定模块的模块.

Fast Near-Duplicate Image Search using Locality Sensitive Hashing

- -
使用LSH快速搜索相似图片,使用LSH的ANN查询按如下方式执行:1)查找查询项的“桶”(哈希值)2)与桶中的每个其他项进行比较. Locality Sensitive Hashing(LSH)是一种有用的工具,即使对于非常大的数据集也可以很好地扩展执行近似最近邻居查询. 深度学习的时代为我们复活了在向量上相似的图像,文本和音频(简单的欧几里得距离)在原始语义内容上也相似(图像的VGG特征向量,文本的Word2Vec).

关于 Java 中 finally 语句块的深度辨析

- myartings - IBM developerWorks 中国 : 文档库
乍看这个题目,是不是有人会问,这个谁不知道啊,大凡熟悉 Java 编程的人都知道 finally 语句块的作用和用法. 事实并非如此,我发现即使写了很多年 Java 程序的人,也不一定能够透彻的理解 finally 语句块. 本篇将以生动形象的案例来带您由浅入深的来分析一下这个小小的 finally,希望这篇文章能够让您真正的理解 finally 语句块的本质,至少阅读完本篇文章后,没有觉得浪费了时间.

Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks | 邹进屹的博客

- -
第三个网络叫ONet,对第二个CNN获得的人脸区域进行再次训练获得是否是人脸,人脸坐标以及五个特征点. 以下项目时MTCNN的具体代码实现. 项目地址:https://github.com/pangyupo/mxnet_mtcnn_face_detection. // MTCNN_VS2015.cpp : 定义控制台应用程序的入口点.

如何使用ALS计算获得item相似度 How to get similar item recommendations using ALS - Quora

- -
不幸的是,Spark ML不支持使用Matrix Factorization模型的item 相似性推荐. Spark不使用Matrix Factorization模型计算item相似度的原因只是该技术不计算item相似性,也不计算用户相似性矩阵. (MF会计算出结果用户因素和项目因素,但不会在这里详细介绍它.