博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于using……的一些探讨
阅读量:6788 次
发布时间:2019-06-26

本文共 4935 字,大约阅读时间需要 16 分钟。

.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 类名称 = NothingTry实体名称 = 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 SubEnd ClassClass 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 SubEnd 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 = 0Try  C$1$0000 = f.IFinally  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 SubEnd ClassClass 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 SubEnd 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是一个引用而非值类型而已!

转载于:https://www.cnblogs.com/ServiceboyNew/archive/2012/08/04/2623247.html

你可能感兴趣的文章
Windows server 2008修改IIS7的文件上传大小限制解决方案
查看>>
读者2013第2期
查看>>
分享:在Server 2008 DNS遇到的Bug
查看>>
php函数substr、mb_substr、mb_strcut截取中文比较
查看>>
静态路由使用下一跳IP与出接口的区别
查看>>
使用lvm-snapshot备份mysql数据库
查看>>
linux下文件系统1
查看>>
SUID SGID 详解
查看>>
JavaScript中var关键字的使用详解
查看>>
Windows SQL2008数据库系列一搭建安装SQL2008R2
查看>>
win2012安装SQL2012安装.net错误解决方法
查看>>
Django1.10 扩展User属性增加头像上传功能
查看>>
Exchange日常管理之二隐藏用户在通讯录中的显示
查看>>
使用escalations限制Nagios报警次数
查看>>
innobackupex 备份 Xtrabackup 增量备份
查看>>
VMware View Client for iPAD、Android发布了!
查看>>
桌面虚拟化之存储加速功能
查看>>
ArgoUML -- 开源UML 建模工具
查看>>
工作中的心态
查看>>
asp.net使用mscharts生成图表
查看>>