从IL认识关键字(三)
关键字
上一篇研究了yield关键字,在本篇研究using关键字。using关键字采用是try-finally结构,本篇主要探讨using结构。
MSDN解释
using 关键字有两个用途:
- 作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。如:
using System.Text;
- 作为语句,用于定义一个范围,在此范围的末尾将释放对象。如:
using (Font font1 = new Font("Arial", 10.0f)) {}
这里我们讨论是第二种情况,作为语句的时候。当作为语句是MSDN上的解释是
定义一个范围,将在此范围之外释放一个或多个对象。
C# IL Code
下面以Font画笔作为例子,反编译IL代码研究。
public void UsingFont() { using (Font font1 = new Font("Arial", 10.0f)) { bool blod = font1.Bold; } }
反编译其IL代码如下:
.method public hidebysig instance void UsingFont() cil managed { .maxstack 3 .locals init ( [0] class [System.Drawing]System.Drawing.Font font, [1] bool flag, [2] bool flag2) L_0000: nop L_0001: ldstr "Arial" L_0006: ldc.r4 10 L_000b: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string, float32) L_0010: stloc.0 L_0011: nop L_0012: ldloc.0 L_0013: callvirt instance bool [System.Drawing]System.Drawing.Font::get_Bold() L_0018: stloc.1 L_0019: nop L_001a: leave.s L_002c L_001c: ldloc.0 L_001d: ldnull L_001e: ceq L_0020: stloc.2 L_0021: ldloc.2 L_0022: brtrue.s L_002b L_0024: ldloc.0 L_0025: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_002a: nop L_002b: endfinally L_002c: nop L_002d: ret .try L_0011 to L_001c finally handler L_001c to L_002c }
从最后一行可以看出是一个try-finally结构,在try之前new对象。上面加亮地方解开我一直以来的困扰(一直以为若using里的对象为空,在finally里调用会在finally里引起异常。所以一直自己手动写try-finally语句代替using,从今天开始可以放心使用using语句),加亮的指令
- 将索引为0的变量(即font)加载到堆栈
- 将null加载到堆栈
- 比较两个值。如果这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;否则,将 0 (int32) 推送到计算堆栈上。
- 取出堆栈顶部值并赋值给索引为2的变量(即flag2)
- 将索引为2(即flag2)加载到堆栈
- 取出堆栈顶部值(即flag2),若为true跳转到L_002b
由上面上面分析可以写出下面代码:
public void UsingCode() { Font font = new Font("Arial", 10.0f); try { bool blod = font.Bold; } finally { if (font != null) { font.Dispose(); } } }
相对于使用using语句,try-finally确实复杂不少,这里不得不佩服微软的语法糖,减少了开发人员的考虑的细节,使代码更加简洁。
验证代码
我们写一个自己画笔,实现IDisposable接口。创建对象方法,创建null和对象。
public class MyFont : IDisposable { #region IDisposable 成员 public void Dispose() { Console.WriteLine("Dispose"); } #endregion
public static MyFont CreateObject(bool isNull) { if (isNull) { Console.WriteLine("is Null"); return null; } Console.WriteLine("is Object"); return new MyFont(); } }
调用方法
static void Main(string[] args) { using (MyFont mf = MyFont.CreateObject(false)) { } using (MyFont mf = MyFont.CreateObject(true)) { } Console.WriteLine("Finish"); Console.Read(); }
运行效果如下:
和我们猜想一致,当创建对象时,调用Dispose方法,当对象为空时,不会调用Dispose方法。
下一篇关键字
以上只是本人的理解与实践,如有错误不足之处希望理解包容,下一篇讨论lock关键字