网站开发沟通,环县网站怎么做,行业网站开发运营方案,首页设计网站 专注本文将介绍以下内容#xff1a; using指令的多种用法using语句在Dispose模式中的应用1. 引言 在.NET大家庭中#xff0c;有不少的关键字承担了多种角色#xff0c;例如new关键字就身兼数职#xff0c;除了能够创建对象#xff0c;在继承体系中隐藏基类成员#xff0c;还在… 本文将介绍以下内容 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 : MarshalByRefObject, ICloneable, ISerializable, IDisposable 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#中。 参考文献 USAJeffrey Richter, Applied Microsoft .NET Framework Programming USABill Wagner, Effective C# 温故知新 [开篇有益] [第一回恩怨情仇is和as] [第二回对抽象编程接口和抽象类] [第三回历史纠葛特性和属性] [第四回后来居上class和struct] [第五回深入浅出关键字---把new说透] [第六回深入浅出关键字---base和this] [第七回品味类型---从通用类型系统开始] [第八回品味类型---值类型与引用类型上内存有理] [第九回品味类型---值类型与引用类型中规则无边] [第十回品味类型---值类型与引用类型下应用征途] [第十一回参数之惑---传递的艺术上] [第十二回参数之惑---传递的艺术下] [第十三回从Hello, world开始认识IL] [第十四回认识IL代码---从开始到现在] [第十五回继承本质论] © 2007 Anytao.com 原创作品转贴请注明作者和出处留此信息。 评论列表 #5楼 2007-10-02 12:05 Lingxi 请问老大 public int GetMaxId() { int maxId 0; using (conn) //conn数据库连接 { string strSQL select max(xxId) from table; try { conn.open(); OracleCommand cmd new OracleCommand(strSQL,conn); OracleDataReader odr cmd.ExecuteReader(); if (odr.Read()) { maxId int.Parse(odr.GetOracleNumber(0).Value.ToString()); } } catch { } } return maxId; } / OracleDataReader odr cmd.ExecuteReader() 是否必须再用using包括起来请您赐教。 支持(0) 反对(0) #6楼 2007-10-02 12:18 idior using可能隐含的问题。http://idior.cnblogs.com/articles/389949.html 支持(0) 反对(0) #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 swFile.CreateText(path)){ ... } 以为用了using就万事大吉了用了using确实会自动释放StreamWriter对象但是在创建StreamWriter对象的时候一样会发生异常应该把整个using语句块用try...catch括起来。 其实没有using之前的经典写法应该是 StreamWriter sw; try { swFile.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 swFile.CreateText(path)){ ... } 以为用了using就万事大吉了用了using确实会自动释放StreamWriter对象但是在创建StreamWriter对象的时候一样会发生异常应该把整个using语句块用try...catch括起来。 其实没有using之前的经典写法应该是 StreamWriter sw; try { swFile.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 swnew StreamWriter(fs)) { ... } } 现在我可以这么写了 using(IDisposable fs new FileStream(, FileMode.Create, FileAccess.Write, FileShare.Write), swnew 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) #36楼 2008-10-31 20:58 syz 请问 如何辨别托管资源与非托管资源using里一般都是非托管资源吗具体包括哪些呢 如果同等的条件下不使用using也不显示的释放资源那么.NET下 对这些非托管资源是如何释放的 支持(0) 反对(0) #37楼 [楼主] 2008-12-20 17:45 Anytao syz .NET平台下的开发都是托管环境你使用的任何类型都是托管代码。而非.NET平台下的资源则是非托管资源其释放过程因不同的情况而异。 支持(0) 反对(0) #38楼 2009-02-20 16:35 feisky using类似于Java语言的import指令都是引入命名空间Java中称作包这种逻辑结构而不同于C语言中的#include指令用于引入实际的类库 ----------------------------------------------------------------- using引入的虽然是命名空间可也可以看作是引入了类库吧 这和C中的include有什么区别呢 支持(0) 反对(0) #39楼 2010-08-04 17:00 风遥 Anytao 35楼你的举例中DBEx de new DBEx(); 这不还没到using就已经抛出异常了吗我也不太明白 内层using块引发异常时将不能释放外层using块的对象资源 支持(0) 反对(0) #40楼 2010-12-08 17:05 Efeng class MoreUsing { public static void Main() { FileEx fe new FileEx(); DBEx de new DBEx(); using (fe) { using (de) { //Do some things here. } } 35楼的应该为下面这样的吧 class MoreUsing { public static void Main() { using (FileEx fe new FileEx()) { using (DBEx de new DBEx()) { //Do some things here. } }