1 finally与return
try-catch-finally是很常用的语法结构,用来控制可能发生异常时的程序流程,其中catch和finally至少要有一个。初学try语法时可能会要问一个问题:如果在try块中return,那么finally还会执行吗?答案是肯定的。这个非常容易验证,就不举例子了。这样带来一些很好的特性,例如我们可以在try块中尝试打开数据库,然后读取数据,然后直接把得到的数据return出去,关闭数据连接的工作就交给finally来做——finally中先判断数据库是否正常打开了,打开了就关闭。这样代码写起来很清晰,每个部分各做各的事。这样我们也可以非常肯定的说,无论发生什么情况(只要不是进程被强行杀掉),finally中的内容一定是要执行的。
那么是不是可以再问一个问题——如果在finally块中也写了return,那么会怎么样呢?试验一下就很容易知道,finally块中是不允许写return的,如果一定要写,就会得到一个编译期错误:
error CS0157: Control cannot leave the body of a finally clause
 
2 先return?先finally?
既然finally一定是要执行的,即使try块中有return,那么这两者的执行顺便是怎么样的呢?简单的做一个实验(下面要说明,这个实验看上去的结果并不这么直观的表现出它的内在):

using System;
public class TestClass1
...{
       public static void Main()
       ...{
              Console.WriteLine("{0}", Func1());
       }
 
       public static int Func1()
       ...{
              int a = 1;
 
              try
              ...{
                     return a;
              }
              finally
              ...{
                     a++;
              }
       }
}
运行这个程序,很容易得到结果为“1”。那么看上去是执行return在先,而finally在后了。真的是这样吗?
例子中我要return的a是一个值类型,那么如果是引用类型,结果又会如何呢?

using System;
public class TestClass2
...{
       public int value = 1;
}
 
public class TestClass1
...{
       public static void Main()
       ...{
              Console.WriteLine("{0}", Func2().value);
       }
 
       public static TestClass2 Func2()
       ...{
              TestClass2 t = new TestClass2();
 
              try
              ...{
                     return t;
              }
              finally
              ...{
                     t.value++;
              }
       }
}
这一次运行的结果并不是1,而是2。显然,运行Func2()返回的结果并不直接是return后面写的t,而是经过finally块执行后值发生变化的t。如何来解释这种区别呢?
3 CLR的栈
要解释这种区别,就需要看看其IL是什么,从调用函数、参数栈的角度来理解。CLR在执行中也有栈,但这个栈的用途与传统的本地代码中的栈并不完全相同。本地代码中栈的用处非常大,不但可以用来临时保存寄存器的值,还用来保存局部变量,此外还用来保存部分或全部传给函数的参数,而函数的返回值一般是通过EAX寄存器来传递的,而不是用栈。但在CLR中,局部变量并非显式的用栈来保存,栈只是用来调用函数时传递参数,此外,函数的返回值也是用栈来保存的。当调用一个函数时,将函数所需要的参数依次压栈,函数里面直接取用这些参数,在函数返回时将返回值压栈,函数返回后,栈顶即是返回值。如果调用者并不关心返回值,那么需要执行一下pop语句,把返回值弹出,这样保证函数在调用前后栈顶的位置是相同的。
当通过压栈传递参数时,参数的类型不同,压栈的内容也不同。如果是值类型,压栈的就是经过复制的参数值,如果是引用类型,那么进栈的只是一个引用,这也就是我们所熟悉的,传递值类型时,函数内修改参数值不会影响函数外,而引用类型的话则会影响。

代码中当我们执行new时,对应的IL是newobj,其结果是创建一个TestClass2类型的对像并返回一个引用放置于栈上,之后的stloc就将这个引用保存为局部变量,于是栈上没有了其他内容。Try块并没有执行太多操作,只是把刚保存的引用再放到栈上,再保存为另一个局部变量,这个局部变量就是稍后要返回的引用,此时我们拥有两个局部变量,但它们是指向同一个对象的两个引用。Finally块先拿出开始时保存的引用放到栈上,dup语句使得栈顶再增加一个完全一样的引用,之后ldfld语句是从栈顶对象取一个成员放到栈上,所取的成员是value,之后再往栈上压一个1,再执行add,就实现了1+1=2的过程,add从栈上弹出两个值,再向栈压回一个值。此时再调用stfld就把刚刚压栈的2设置给栈上2之下的那个引用所指对象的value属性上。而在finally之后的部分才是真正的return,它试图取出我们所保存的第二个局部变量压栈,将它作为返回值。但对于引用类型来说,它与先前所操作的引用所指的是同一对象,因此finally块中的操作会影响到返回值,也就非常好理解了。
 
4 改编
知道了finally与return的实现原理,也就不难做出进一步的推广。例如把程序改成这样(返回时由直接返回t变为在t上调用一个做一些操作后返回自己的函数),其执行结果也不难猜出来吧:

using System;
public class TestClass2
...{
       public int value = 1;
 
       public TestClass2 Double()
       ...{
              value *= 2;
              return this;
       }
}
 
public class TestClass1
...{
       public static void Main()
       ...{
              Console.WriteLine("{0}", Func2().value);
       }
 
       public static TestClass2 Func2()
       ...{
              TestClass2 t = new TestClass2();
 
              try
              ...{
                     return t.Double();
              }
              finally
              ...{
                     t.value++;
              }
       }
}

转载于:https://www.cnblogs.com/whz881027/articles/2106981.html

C# try与finally(WinForm、Asp.Net)相关推荐

  1. 该系列主要整理收集在使用C#开发WinForm应用文章及相关代码来源于WinForms小组...

    该系列主要整理收集在使用C#开发WinForm应用文章及相关代码, 平时看到大家主要使用C#来开发Asp.Net应用,这方面的文章也特别多,而关于WinForm的文章相对少很多,而自己对WinForm ...

  2. [转载]C# WinForm开发系列 - 文章索引

    该系列主要整理收集在使用C#开发WinForm应用文章及相关代码, 平时看到大家主要使用C#来开发Asp.Net应用,这方面的文章也特别多,而关于WinForm的文章相对少很多,而自己对WinForm ...

  3. (转)ASP.NET Core 企业开发架构概述

    目录 一. 垂直方向架构 1. 多层架构 2. 典型框架 二. 水平方向架构 1. SOA架构 2. 微服务架构 3. 整体式 vs SOA架构 vs 微服务架构 4. SOA典型框架 5. 微服务典 ...

  4. C# WinForm开发系列

    文章来源:博客园 该系列主要整理收集在使用C#开发WinForm应用文章及相关代码, 平时看到大家主要使用C#来开发Asp.Net应用,这方面的文章也特别多,而关于WinForm的文章相对少很多,而自 ...

  5. ASP.NET Core 企业开发架构概述

    ASP.NET Core 企业开发架构概述 企业开发框架包括垂直方向架构和水平方向架构.垂直方向架构是指一个应用程序的由下到上叠加多层的架构,同时这样的程序又叫整体式程序.水平方向架构是指将大应用分成 ...

  6. 基于WF4的新平台-表单格式

    表单格式 目录 概述    2 表单映射    3 方式1    3 方式2    3 表单创建    5 主子表单    5 计算字段    6 选项级联填充    7 概述 这里所说的表单与数据库 ...

  7. ActiveReports 报表应用教程 (1)-Hello ActiveReports

    在开始专题内容之前,我们还是了解一下 ActiveReports 是一款什么产品:ActiveReports是一款在全球范围内应用非常广泛的报表控件,以提供.NET报表所需的全部报表设计功能领先于同类 ...

  8. 免费资源 | ActiveReports 报表控件发布多平台 Demo 代码集合

    近期,ActiveReports 产品开发组的小伙伴针对大家比较关注的报表功能.常见问题.经典实现,特意准备了一个Demo代码集合,涉及WinFormss \ ASP.NET \ MVC 多个技术平台 ...

  9. 无聊,写写工作日记吧.

    开始对不同开发语言,开发框架的分析,以确定未来系统的依赖方向. 1.备选语言有:C++,C#. 2.备选框架有:MFC(vs2008),.NET,WINFORM,ASP.NET. 面对以上的条件,可以 ...

  10. MVC的概念及MVC 3.0开发环境

    摘要 MVC即: Model(模型), View(视图), Controller(控制器) Model:模型对象是实现应用程序数据域逻辑的应用程序部件. 通常,模型对象会检索模型状态并将其存储在数据库 ...

最新文章

  1. 我的hadoop学习之路
  2. 在windows下基于visual studio2017和CMake的安装Google glog
  3. mysql+提升更新语句效率_MySQL加快批量更新 UPDATE优化
  4. ARM 之九 Cortex-M/R 内核启动过程 / 程序启动流程(基于ARMCC、Keil)
  5. python 主语_前深度学习时代--FFM模型的原理与Python实现
  6. 乱序图片 极验_极验验证吴渊:传统图片验证方式已经无效了!
  7. 关于在头文件中定义变量
  8. 开始把一些东西放到博客上
  9. 分享一个dapper简单封装
  10. 初中数学抽象教学的案例_初中数学教学反思案例
  11. java mysql jdbc 封装_利用Java针对MySql封装的jdbc框架类 JdbcUtils 完整实现(包括增删改查、JavaBean反射原理,附源代码)...
  12. Python元组 ()
  13. python自动生成文章原创_【Python】皮皮AI工具( AI文章伪原创工具)
  14. 计算机浏览记录删除,如何完全删除本地计算机上的浏览历史记录?如何从计算机中完全删除Internet访问...
  15. 为什么不同文化中都会发现圆形房屋
  16. 遥感影像地图编制流程
  17. python2.7 get-pip.py总是网络超时
  18. Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs;
  19. SOFA Weekly |社区开发者的搬砖日常、QA 整理、新手任务计划
  20. 门限签名技术火了,它的硬核之处绝不仅仅是秘密共享……

热门文章

  1. 机器学习:分类,回归,聚类
  2. 干货丨大规模机器学习框架的四重境界(经典长文,值得收藏)
  3. (已解决)linux如何删除-开头的文件或者目录
  4. python库datetime的使用
  5. 2022年十大卫星技术创新及发展趋势
  6. “机器人迟钝一点,会更有人情味”,迪士尼提出新型人机交互系统
  7. 王飞跃:平行汽车到平行驾驶,从“功能汽车”到“智能汽车”
  8. 当互联网人决定躺平......
  9. “我的开源项目被威胁了!”
  10. 我坦白!我是第五位飞上太空的程序员游客