zoukankan      html  css  js  c++  java
  • 关于using……的一些探讨

    .NET中c#或者vb.net之所以那么吸引人,其重要原因之一就是其存在大量的“简化写法”——这些“简化写法”主要通过“语法糖”的形式存在(比如Lambda,匿名方法等……)。今天我们来探讨一下using……这个“语法糖”的本质和用法,以及一些常见的错误。

    1)using……用法:

    在C#或者是VB.NET中“using”必须和一个实现了IDisposible接口的实体类混合使用,其语法形式为:

    [C#]

    using(类名称 实体名称 = new 类名称())
    {
        //这里使用……
    }

    [VB.NET]

    Using 实体名 As New 类名称
    '这儿使用这个类实体……
    End Using

    通过MSDN的说明我们可以知道using的本质是一个try……finally:
    [C#]

    类名称 实体名称 = null;
    try
    {
      实体名称 = new 类名称();
    }
    finally
    {
      实体名称.Dispose();
    }

    [VB.NET]

    Dim 实体名称 As 类名称 = Nothing
    Try
    实体名称 = New 类名称()
    Finally
    实体名称.Dispose()
    End Try

    一般地,凡是使用using块的实体只能在using作用域中使用(即只允许在using的{……}中使用)。其实从真实情况下的try……finally……中也可以得出这样一个结论——因为无论如何,Finally必须被执行,也就是说Dispose被调用之后实体对象(内部一些托管或非托管对象实体等)已经被销毁,虽然“类实体”自身并未被回收,但是此类已经无法再使用了。

    下面在进行一下拓展:

    【例】,问以下程序运行的结果:

    [C#]

    class Fun:IDisposable
    {
       public int I{get;set;}
       public Fun()
        {
             I=1;
        }
       public void Dispose()
       {
              I=0;
       }    
    }
    
    class MainTest
    {
        static int Func()
        {
              using(Fun f = new Fun())
             {
                     return f.I;
             }
        }
        static void Main()
        {
             Console.WriteLine(Func());
        }
    }

    [VB.NET]

    Class Fun
        Implements IDisposable
        Public Property I() As Integer
            Get
                Return m_I
            End Get
            Set
                m_I = Value
            End Set
        End Property
        Private m_I As Integer
        Public Sub New()
            I = 1
        End Sub
        Public Sub Dispose()
            I = 0
        End Sub
    End Class
    
    Class MainTest
        Private Shared Function Func() As Integer
            Using f As New Fun()
                Return f.I
            End Using
        End Function
        Private Shared Sub Main()
            Console.WriteLine(Func())
        End Sub
    End Class

    粗看此题目,或许你会认为说因为Finally无论如何必须要执行,因此I从1变成了0,输出的也是0——其实不然!在using中如果使用了“return”之后,内部生成的代码略有变化(我们通过IL来证明):

    .method private hidebysig static int32  Func() cil managed
    {
      // 代码大小       36 (0x24)
      .maxstack  2
      .locals init ([0] class CSharp.Fun f,
               [1] int32 CS$1$0000,
               [2] bool CS$4$0001)
      IL_0000:  nop
      IL_0001:  newobj     instance void CSharp.Fun::.ctor()
      IL_0006:  stloc.0
      .try
      {
        IL_0007:  nop
        IL_0008:  ldloc.0
        IL_0009:  callvirt   instance int32 CSharp.Fun::get_I()
        IL_000e:  stloc.1
        IL_000f:  leave.s    IL_0021
      }  // end .try
      finally
      {
        IL_0011:  ldloc.0
        IL_0012:  ldnull
        IL_0013:  ceq
        IL_0015:  stloc.2
        IL_0016:  ldloc.2
        IL_0017:  brtrue.s   IL_0020
        IL_0019:  ldloc.0
        IL_001a:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
        IL_001f:  nop
        IL_0020:  endfinally
      }  // end handler
      IL_0021:  nop
      IL_0022:  ldloc.1
      IL_0023:  ret
    } // end of method MainTest::Func

    注意“try“这一部分的代码——首先ldloc把实体类压入栈中,随后调用get_I(内部生成的函数,用于取出属性I的私有变量数值),随后弹栈把I的内容存入到CS$1$0000中(因为是值类型,因此明显是复制原有的内容)。同时注意在endfinally后ldloc.1弹栈取出CS$1$000的内容(因此仍然为1)。 我们另外可以注意到return的部分是在try……finally……之后才返回的。因此我们可以这样理解Fun静态函数中的内容:

    [C#]

    int C$1$0000 = 0;
    try
    {
       C$1$0000 = f.I;
    }
    finally
    {
       f.Dispose();
    }
    return C$1$0000;

    [VB.NET]

    Dim C$1$0000 As Integer = 0
    Try
      C$1$0000 = f.I
    Finally
      f.Dispose()
    End Try
    Return C$1$0000

    我们发现“C$1$0000”是自生成的(因为需要把p.I压入堆栈的需要)。因此实际返回的是这个临时生成的变量值并非原来的p.I(一般地函数需要返回某个值,都是需要临时在栈中生成一个空间存放这个变量值,然后在弹出返回)。

    以此类推,如果try中返回的是一个引用类型(且这个引用类型在finally中受Dispose影响发生了改变),则即便临时栈中生成了一个引用副本(本质还是引用原来的实体!),并且返回这个副本,由于引用的缘故自然还是以最终的finally后受影响的为准。如下例子(读者自定比较):

    [C#]

    class Fun:IDisposable
    {
       public int I{get;set;}
       public Fun()
        {
             I=1;
        }
       public void Dispose()
       {
              I=0;
       }    
    }
    
    class MainTest
    {
        static Fun Func()
        {
              using(Fun f = new Fun())
             {
                     return f;
             }
        }
        static void Main()
        {
             Console.WriteLine(Func().I);
        }
    }

    [VB.NET]

    Class Fun
        Implements IDisposable
        Public Property I() As Integer
            Get
                Return m_I
            End Get
            Set
                m_I = Value
            End Set
        End Property
        Private m_I As Integer
        Public Sub New()
            I = 1
        End Sub
        Public Sub Dispose()
            I = 0
        End Sub
    End Class
    
    Class MainTest
        Private Shared Function Func() As Fun
            Using f As New Fun()
                Return f
            End Using
        End Function
        Private Shared Sub Main()
            Console.WriteLine(Func().I)
        End Sub
    End Class

    转化对应的IL代码:

     // 代码大小       31 (0x1f)
      .maxstack  2
      .locals init ([0] class CSharp.Fun f,
               [1] class CSharp.Fun CS$1$0000,
               [2] bool CS$4$0001)
      IL_0000:  nop
      IL_0001:  newobj     instance void CSharp.Fun::.ctor()
      IL_0006:  stloc.0
      .try
      {
        IL_0007:  nop
        IL_0008:  ldloc.0
        IL_0009:  stloc.1
        IL_000a:  leave.s    IL_001c
      }  // end .try
      finally
      {
        IL_000c:  ldloc.0
        IL_000d:  ldnull
        IL_000e:  ceq
        IL_0010:  stloc.2
        IL_0011:  ldloc.2
        IL_0012:  brtrue.s   IL_001b
        IL_0014:  ldloc.0
        IL_0015:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
        IL_001a:  nop
        IL_001b:  endfinally
      }  // end handler
      IL_001c:  nop
      IL_001d:  ldloc.1
      IL_001e:  ret
    } // end of method MainTest::Func

    几乎与第一个例子差不多,只不过CS$1$0000是一个引用而非值类型而已!

  • 相关阅读:
    B
    A
    I
    IIS发布和部署
    编程中什么叫上下文
    浅谈Session,Cookie和http协议中的无状态
    cmd界面输入sqlplus提示不是内外部命令解决方法
    C#已设置安全调试选项,但此选项要求的VS承载进程在此调试中不可用。解决方法
    IIS和IIS Express的区别
    vue.js:634 [Vue warn]: Failed to generate render function: SyntaxError: Unexpected token ')'
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2623247.html
Copyright © 2011-2022 走看看