从IL认识关键字(三)

关键字

     上一篇研究了yield关键字,在本篇研究using关键字。using关键字采用是try-finally结构,本篇主要探讨using结构。

MSDN解释

  using 关键字有两个用途:

  1. 作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。如:
    using System.Text;
  2. 作为语句,用于定义一个范围,在此范围的末尾将释放对象。如:
    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语句),加亮的指令

  1. 将索引为0的变量(即font)加载到堆栈
  2. null加载到堆栈
  3. 比较两个值。如果这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;否则,将 0 (int32) 推送到计算堆栈上。
  4. 取出堆栈顶部值并赋值给索引为2的变量(即flag2)
  5. 将索引为2(即flag2)加载到堆栈
  6. 取出堆栈顶部值(即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关键字

原文地址:https://www.cnblogs.com/WilsonPan/p/2909488.html