[你必须知道的.NET]第十六回:深入浅出关键字---using全接触

本文将介绍以下内容:

  • using指令的多种用法
  • using语句在Dispose模式中的应用

 

 

 

1. 引言

在.NET大家庭中,有不少的关键字承担了多种角色,例如new关键字就身兼数职,除了能够创建对象,在继承体系中隐藏基类成员,还在泛型声明中约束可能用作类型参数的参数,在[第五回:深入浅出关键字---把new说透]我们对此都有详细的论述。本文,将把目光转移到另外一个身兼数职的明星关键字,这就是using关键字,在详细讨论using的多重身份的基础上来了解.NET在语言机制上的简便与深邃。

那么,using的多重身份都体现在哪些方面呢,我们先一睹为快吧:

·引入命名空间

·创建别名

·强制资源清理

下面,本文将从这几个角度来阐述using的多彩应用。

2. 引入命名空间

using作为引入命名空间指令的用法规则为:

using Namespace;

在.NET程序中,最常见的代码莫过于在程序文件的开头引入System命名空间,其原因在于System命名空间中封装了很多最基本最常用的操作,下面的代码对我们来说最为熟悉不过:

using System;

这样,我们在程序中就可以直接使用命名空间中的类型,而不必指定详细的类型名称。using指令可以访问嵌套命名空间。

关于:命名空间

命名空间是.NET程序在逻辑上的组织结构,而并非实际的物理结构,是一种避免类名冲突的方法,用于将不同的数据类型组合划分的方式。例如,在.NET中很多的基本类型都位于System命名空间,数据操作类型位于System.Data命名空间,

误区:

·using类似于Java语言的import指令,都是引入命名空间(Java中称作包)这种逻辑结构;而不同于C语言中的#include指令,用于引入实际的类库,

·using引入命名空间,并不等于编译器编译时加载该命名空间所在的程序集,程序集的加载决定于程序中对该程序集是否存在调用操作,如果代码中不存在任何调用操作则编译器将不会加载using引入命名空间所在程序集。因此,在源文件开头,引入多个命名空间,并非加载多个程序集,不会造成“过度引用”的弊端。

3. 创建别名

using为命名空间创建别名的用法规则为:

using alias = namespace | type;

其中namespace表示创建命名空间的别名;而type表示创建类型别名。例如,在.NET Office应用中,常常会引入Microsoft.Office.Interop.Word.dll程序集,在引入命名空间时为了避免繁琐的类型输入,我们通常为其创建别名如下:

using MSWord = Microsoft.Office.Interop.Word;

这样,就可以在程序中以MSWord来代替Microsoft.Office.Interop.Word前缀,如果要创建Application对象,则可以是这样,

        private static MSWord.Application ooo = new MSWord.Application();

同样,也可以创建类型的别名,用法为:

    using MyConsole = System.Console;

    class UsingEx

    {

        public static void Main()

        {

            MyConsole.WriteLine("应用了类的别名。");

        }

    }

而创建别名的另一个重要的原因在于同一cs文件中引入的不同命名空间中包括了相同名称的类型,为了避免出现名称冲突可以通过设定别名来解决,例如:
 

namespace Boyspace

{

    public class Player

    {

        public static void Play()

        {

            System.Console.WriteLine("Boys play football.");

        }

    }

}

namespace Girlspace

{

    public class Player

    {

        public static void Play()

        {

            System.Console.WriteLine("Girls play violin.");

        }

    }

}


    以using创建别名,有效的解决了这种可能的命名冲突,尽管我们可以通过类型全名称来加以区分,但是这显然不是最佳的解决方案,using使得这一问题迎刃而解,不费丝毫功夫,同时在编码规范上看来也更加的符合编码要求。

4. 强制资源清理

4.1 由来

要理解清楚使用using语句强制清理资源,就首先从了解Dispose模式说起,而要了解Dispose模式,则应首先了解.NET的垃圾回收机制。这些显然不是本文所能完成的宏论,我们只需要首先明确的是.NET提供了Dispose模式来实现显式释放和关闭对象的能力。

Dispose模式

Dispose模式是.NET提供的一种显式清理对象资源的约定方式,用于在.NET 中释放对象封装的非托管资源。因为非托管资源不受GC控制,对象必须调用自己的Dispose()方法来释放,这就是所谓的Dispose模式。从概念角度来看,Dispose模式就是一种强制资源清理所要遵守的约定;从实现角度来看,Dispose模式就是让要一个类型实现IDisposable接口,从而使得该类型提供一个公有的Dispose方法。

本文不再讨论如何让一个类型实现Dispose模式来提供显示清理非托管资源的方式,而将注意集中在如何以using语句来简便的应用这种实现了Dispose模式的类型的资源清理方式。我们在内存管理与垃圾回收章节将有详细的讨论。

using语句提供了强制清理对象资源的便捷操作方式,允许指定何时释放对象的资源,其典型应用为:

            using (Font f = new Font("Verdana", 12, FontStyle.Regular))

            {

                //执行文本绘制操作

                Graphics g = e.Graphics;

                Rectangle rect = new Rectangle(10, 10, 200, 200);

                g.DrawString("Try finally dispose font.", f, Brushes.Black, rect);

            }//运行结束,释放f对象资源

在上述典型应用中,using语句在结束时会自动调用欲被清除对象的Dispose()方法。因此,该Font对象必须实现IDispose接口,才能使用using语句强制对象清理资源。我们查看其类型定义可知:

public sealed class Font : MarshalByRefObjectICloneableISerializableIDisposable

Font类型的确实现了IDisposeable接口,也就具有了显示回收资源的能力。然而,我们并未从上述代码中,看出任何使用Dispose方法的蛛丝马迹,这正式using语句带来的简便之处,其实质究竟怎样呢?

4.2 实质

要想了解using语句的执行本质,了解编译器在背后做了哪些手脚,就必须回归到IL代码中来揭密才行:

.method public hidebysig static void Main() cil managed

{

 .entrypoint

 // 代码大小       40 (0x28)

 .maxstack 4

 .locals init ([0] class [System.Drawing]System.Drawing.Font f,

           [1] bool CS$4$0000)

 IL_0000: nop

 IL_0001: ldstr      "Verdana"

 IL_0006: ldc.r4     12.

 IL_000b: ldc.i4.0

 IL_000c: newobj     instance void [System.Drawing]System.Drawing.Font::.ctor(string,float32,

     valuetype [System.Drawing]System.Drawing.FontStyle)

 IL_0011: stloc.0

 .try

 {

……部分省略……

 } // end .try

 finally

 {

……部分省略……

    IL_001f: callvirt   instance void [mscorlib]System.IDisposable::Dispose()

    IL_0024: nop

    IL_0025: endfinally

 } // end handler

 IL_0026: nop

 IL_0027: ret

} // end of method UsingDispose::Main

显然,编译器在自动将using生成为try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源。

在.NET规范中,微软建议开放人员在调用一个类型的Dispose()或者Close()方法时,将其放在异常处理的finally块中。根据上面的分析我们可知,using语句正是隐式的调用了类型的Dispose方法,因此以下的代码和上面的示例是完全等效的:

            Font f2 = new Font("Arial", 10, FontStyle.Bold);

            try

            {

//执行文本绘制操作

                Graphics g = new Graphics();

                Rectangle rect = new Rectangle(10, 10, 200, 200);

                g.DrawString("Try finally dispose font.", f2, Brushes.Black, rect);  

            }

            finally

            {

                if (f2 != null)

                    ((IDisposable)f2).Dispose();

            }

4.3 规则

·using只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口的类型使用using语句,否则会出现编译时错误;

·using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finnaly来实现,因为嵌套的using语句可能存在隐藏的Bug。内层using块引发异常时,将不能释放外层using块的对象资源。

·using语句支持初始化多个变量,但前提是这些变量的类型必须相同,例如:

            using(Pen p1 = new Pen(Brushes.Black), p2 = new Pen(Brushes.Blue))

            {

                //

            }

否则,编译将不可通过。不过,还是有变通的办法来解决这一问题,原因就是应用using语句的类型必然实现了IDisposable接口,那么就可以以下面的方式来完成初始化操作,

            using (IDisposable font = new Font("Verdana", 12, FontStyle.Regular), pen = new Pen(Brushes.Black))

            {

                float size = (font as Font).Size;

                Brush brush = (pen as Pen).Brush;

            }

另一种办法就是以使用try-finally来完成,不管初始化的对象类型是否一致。

·Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存依然由垃圾回收器控制。

·程序在达到using语句末尾时退出using块,而如果到达语句末尾之前引入异常则有可能提前退出。

·using中初始化的对象,可以在using语句之前声明,例如:

            Font f3 = new Font("Verdana", 9, FontStyle.Regular);

            using (f3)

            {

                //执行文本绘制操作

            }

5. 结论

一个简单的关键字,多种不同的应用场合。本文从比较全面的角度,诠释了using关键字在.NET中的多种用法,值得指出的是这种用法并非实现于.NET的所有高级语言,本文的情况主要局限在C#中。

  

 

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)Bill WagnerEffective C#

 

温故知新

[开篇有益]
[第一回:恩怨情仇:is和as]
[第二回:对抽象编程:接口和抽象类]
[第三回:历史纠葛:特性和属性]
[第四回:后来居上:class和struct]
[第五回:深入浅出关键字---把new说透]
[第六回:深入浅出关键字---base和this]
[第七回:品味类型---从通用类型系统开始]
[第八回:品味类型---值类型与引用类型(上)-内存有理]
[第九回:品味类型---值类型与引用类型(中)-规则无边]
[第十回:品味类型---值类型与引用类型(下)-应用征途]
[第十一回:参数之惑---传递的艺术(上)]
[第十二回:参数之惑---传递的艺术(下)]
[第十三回:从Hello, world开始认识IL]
[第十四回:认识IL代码---从开始到现在]
[第十五回:继承本质论]

© 2007 Anytao.com

原创作品,转贴请注明作者和出处,留此信息。

 

评论列表
  
  
#7楼 
[楼主] 2007-10-02 16:22 Anytao
@Lingxi 
赐教不敢,只是一点个人的想法。 
关于使用using进行资源清理,应该基于以下几点: 
1 是否实现了IDispose接口,这点上OracleDataReader对象是没有问题的; 
2 最好不要嵌套using块,否则可能引起外层资源不能释放的Bug,因此这里在conn内部嵌套using块会导致可能的异常; 
3 对象本身是否需要进行显式的资源释放,常见的情况通常集中在例如:数据库连接、文件、互斥体、嵌套字、位图这样的非托管资源上。 
在你的示例中using(conn)能够保证关闭数据库连接,datareader和Connection是紧密关联的,因此没有必要再次提供显式的关闭操作。建议以OracleDataReader odr = cmd.ExecuteReader(CommandBehavior.CloseConnection)方式来控制关闭。 

推荐一篇参考文章: 
http://www.cnblogs.com/kentyshang/archive/2006/09/26/514901.html 
支持(0) 反对(0)
  
  
#34楼 
2008-04-18 21:17 文祥
很奇怪很多示例代码都用了这样的写法: 
using(StreamWriter sw=File.CreateText(path)){ 
... 

以为用了using就万事大吉了,用了using确实会自动释放StreamWriter对象,但是在创建StreamWriter对象的时候一样会发生异常,应该把整个using语句块用try...catch括起来。 
其实没有using之前的经典写法应该是: 
StreamWriter sw; 
try { 
sw=File.CreateText(path); 

catch(Exception e) { 
Console.WriteLine(e.Message); 
return; 

try { 
sw.Write(...);... 

catch(Exception e) { 
Console.WriteLine(e.Message); 

finally { 
sw.Dispose(); 


对于你说的“内层using块引发异常时,将不能释放外层using块的对象资源。”这句话,我深表怀疑,于是写了一段代码测试: 
public class Program { 
static void Main(string[] args) { 
try { 
using (MyDisposeClass mdc1 = new MyDisposeClass(1)) { 
using (MyDisposeClass mdc2 = new MyDisposeClass(2)) { 
throw new Exception(); 



catch { } 


class MyDisposeClass : IDisposable { 
private int n; 
public MyDisposeClass(int m) { 
this.n = m; 

public void Dispose() { 
Console.WriteLine(n); 


输出为:2 1。不管有没有try...catch块,位置放在哪里,都会调用两个MyDisposeClass对象的Dispose方法,这证明了在using中的对象确实一定会调用Dispose方法,也即是在finally块中的语句一定会执行。 

很奇怪很多示例代码都用了这样的写法: 
using(StreamWriter sw=File.CreateText(path)){ 
... 

以为用了using就万事大吉了,用了using确实会自动释放StreamWriter对象,但是在创建StreamWriter对象的时候一样会发生异常,应该把整个using语句块用try...catch括起来。 
其实没有using之前的经典写法应该是: 
StreamWriter sw; 
try { 
sw=File.CreateText(path); 

catch(Exception e) { 
Console.WriteLine(e.Message); 
return; 

try { 
sw.Write(...);... 

catch(Exception e) { 
Console.WriteLine(e.Message); 

finally { 
sw.Dispose(); 


对于你说的“内层using块引发异常时,将不能释放外层using块的对象资源。”这句话,我深表怀疑,于是写了一段代码测试: 
public class Program { 
static void Main(string[] args) { 
try { 
using (MyDisposeClass mdc1 = new MyDisposeClass(1)) { 
using (MyDisposeClass mdc2 = new MyDisposeClass(2)) { 
throw new Exception(); 



catch { } 


class MyDisposeClass : IDisposable { 
private int n; 
public MyDisposeClass(int m) { 
this.n = m; 

public void Dispose() { 
Console.WriteLine(n); 


输出为:2 1。不管有没有try...catch块,位置放在哪里,都会调用两个MyDisposeClass对象的Dispose方法,这证明了在using中的对象确实一定会调用Dispose方法,也即是在finally块中的语句一定会执行 

在using语句声明多个变量确实挺有意思,而且还可以用一个共同的接口作类型,这我绝对想不到,这种写法解决了我的一个问题,我原来这么写: 
using(FileStream fs = new FileStream("", FileMode.Create, FileAccess.Write, FileShare.Write)) { 
using(StreamWriter sw=new StreamWriter(fs)) { 
... 


现在我可以这么写了: 
using(IDisposable fs = new FileStream("", FileMode.Create, FileAccess.Write, FileShare.Write), sw=new StrreamWriter((FileStream)fs)) {...} 
哈哈。
支持(0) 反对(0)
  
#35楼 
[楼主] 2008-04-18 23:43 Anytao
@文祥 
谢谢文祥的分析,我所说的“内层using块引发异常时,将不能释放外层using块的对象资源”,其实表达的是一种模式的推荐,例如有下面情况存在时,应用using模式就有可能带来问题: 


public class FileEx: IDisposable 

public void Dispose() 

Console.WriteLine("FileEx资源清理。"); 
Console.Read(); 



public class DBEx : IDisposable 

public DBEx() 

//To throw exception in constructor. 
throw new Exception(); 



public void Dispose() 

Console.WriteLine("DBEx资源清理。"); 
Console.Read(); 



class MoreUsing 

public static void Main() 


FileEx fe = new FileEx(); 
DBEx de = new DBEx(); 

using (fe) 

using (de) 

//Do some things here. 






支持(0) 反对(0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438737.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为双系统是鸿蒙系统吗,华为p50pro是鸿蒙系统吗-华为p50pro有双系统吗

华为p50pro的外观基本上就是延续了上一代的风格,没有什么太大的变化,不过影像能力还是非常令人期待的,下面一起来了解华为p50pro的系统方面,看看有没有你暂所不知的消息。近日,有消息曝光了华为 P50 Pro将会有 Harmony…

[你必须知道的.NET]第十七回:貌合神离:覆写和重载

本文将介绍以下内容: 什么是覆写,什么是重载覆写与重载的区别覆写与重载在多态特性中的应用1. 引言 覆写(override)与重载(overload),是成就.NET面向对象多态特性的基本技术之一,两…

鸿蒙系统正式开源,余承东:鸿蒙系统正式开源,友商也可以使用!

鸿蒙OS2.0正式开源:从PPT走向前台,该谁脸红了?在9月10日的开发者大会上,华为鸿蒙2.0发布,已经不再是某些人嘴中的PPT、又哄又蒙的鸿蒙了。说鸿蒙是PPT的言论,在前不久还能看到,如今鸿蒙已经发布…

[你必须知道的.NET]第十八回:对象创建始末(上)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究1. 引言 了解.NET的内存管理机制,首先应该从内存分配开始,也就是对象的创建环节。对象的创建,是个复杂的过程,主要包括内存分配和初始化两个环节。例如&…

[你必须知道的.NET]第十九回:对象创建始末(下)

本文将介绍以下内容: 对象的创建过程内存分配分析内存布局研究接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例分配于托管堆上,而线…

android material 颜色值,Android Material Colors 谷歌 Material Design 标准颜色

Android Material Colors谷歌 Material Design 标准颜色。调色板资源文件通过 doc-getter 自动抓取生成。运行 Demo 来查看效果。UsageGradlecompile com.takwolf.android.materialdesign:color:0.0.1Stylecolor/material_indigo_500color/material_indigo_700color/material_p…

突然吐字不清_要注意说话吐字不清小心是脑中风前兆

任何疾病发病之前往往会有一些前兆出现,像是脑中风这种疾病在发作之前也是有前兆的,若是朋友们能够尽早的发现就能够在发病前进行治疗了。朋友们要注意的是说话吐字不清小心是脑中风前兆,这是脑中风发病前的典型前兆,还有头晕、呕…

c++ vs release没有exe_未来安全 | 第一次Geant4培训总结 | 有没有你关注的问题呢?...

Geant4简介Geant4是蒙卡工具包,模拟很多粒子,记录一些统计量,用这些统计量去估计真实的物理实验的结果。蒙卡模拟程序,从最老的MCNP,到PENELOPE,FLUKA等。MCNP是用输入卡片(输入文件)实现的,在一…

[你必须知道的.NET]第二十一回:认识全面的null

说在,开篇之前 null、nullable、??运算符、null object模式,这些闪亮的概念在你眼前晃动,我们有理由相信“存在即合理”,事实上,null不光合理,而且重要。本文,从null的基本认知开始&#xff0…

html用表格做个人主页页面,利用HTML的表格进行页面布局

在DIVCSS布局出现前,基本上所用的网站都使用table来进行布局。因为table布局很简单,但是table布局不灵活且代码很多。下面将介绍怎样使用table来进行布局。实例:我们来布局一个常见网站后台程序的架构。布局图如下所示:实例代码&a…

cesium坡度坡向分析_景观设计分析图制作技巧到底是什么?

国外设计中,人们都开始用动态分析图啦厉害的不要不要啊!如果你也想做如此高逼格的分析图记得往下看!景观设计分析为:人文,背景,区位,现状,历史,功能,流线&…

采购模板html5,蓝色的采购信息管理系统手机界面wap模板

手机版大气信息管理系统界面模板,采购信息管理wap手机模板下载。资源下载此资源下载价格为4D币,请先登录资源文件列表codedown123-080801-25/business_log.html , 6657codedown123-080801-25/choose.html , 6869codedown123-080801-25/css/animate.css ,…

adobe audition cs6 能打开mpcm文件吗?_Adobe全家桶出现这些漏洞,赶紧上官网下载补丁吧...

导语:Adobe已发布了计划的2020年7月安全更新,涵盖了五个不同产品领域的缺陷:Creative Cloud Desktop;媒体编码器;下载管理器; 真正的服务;和ColdFusion。其中四个错误的严重性被评为严重,而其他…

详解CSS的相对定位和绝对定位

CSS的相对定位和绝对定位 一、Static 静态定位 通常情况下,我们元素的position属性的值默认为static 就是没有定位,元素出现在正常的文档流中,这个时候你给这个元素设置的left,right,bottom,top这些偏移属性都是没有效果的,不会生…

观看实验中微型计算机虚拟拆装演示,虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0...

下面我们对虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0文件阐述相关使用资料和虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0文件的更新信息。虚拟仿真实验 北斗一号微机原理虚拟仿真实验系统64位 v3.0“微机原理虚拟仿真实验”适用于《微机原理》《微…

[你必须知道的.NET]第二十二回:字符串驻留(上)---带着问题思考

走钢丝的人,在刺激中体验快感。带着问题思考,在问题上迸发火花。 或者给问题以答案,或者给答案以问题,你可能永远无法看清全部,但是总能从一点突破很多。事实的关键就在于面对问题,我该如何思考&#xff1…

springboot超详细教程_超详细便当袋教程 || 特殊时期,自己带饭最安心!

持续受疫情影响,闷在家里的广大网友们早就坐不住了。尤其是最近各地复工陆续开始,小心心是不是开始躁动了?终于可以出门放飞自我,放肆吃吃喝喝了嘛?再忍一忍呀同志们!疫情还没结束,病毒还没被消…

[你必须知道的.NET]第二十三回:品味细节,深入.NET的类型构造器

1 引言今天Artech兄在《关于Type Initializer和 BeforeFieldInit的问题,看看大家能否给出正确的解释》一文中让我们认识了一个关于类型构造器调用执行的有趣示例,其中也相应提出了一些关于beforefieldinit对于类型构造器调用时机的探讨,对于我…

[你必须知道的.NET]第二十四回:认识元数据和IL(上)

说在,开篇之前很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有《第一回:恩怨情仇:is和as…

计算机无法找到组件c0000135,电脑显示没有找到dwmapi.dll组件怎么办?计算机丢失dwmapi.dll的处理方法...

很多用户在操作Windows系统的过程中发现“没有找到dwmapi.dll”,如果丢失dwmapi.dll组件会导致应用程序无法运行。其实,大家可以尝试在相关网站下载所缺少的组件,或者是通过第三方软件来进行安装下载,这里小编带领大家看看具体解决…