using关键字的主要3个作用:
- 引入命名空间
- 创建别名
- 强制资源清理
一、引入命名空间:
命名空间是.NET程序在逻辑上的组织结构,而并非实际的物理结构,是一种避免类名冲突的方法,用于将不同的数据类型组合划分的方式。例如,在.NET中很多的基本类型都位于System命名空间,数据操作类型位于System.Data命名空间。
using类似于Java语言的import指令,都是引入命名空间(Java中称作包)这种逻辑结构;而不同于C语言中的#include指令,用于引入实际的类库,using引入命名空间,并不等于编译器编译时加载该命名空间所在的程序集,程序集的加载决定于程序中对该程序集是否存在调用操作,如果代码中不存在任何调用操作则编译器将不会加载using引入命名空间所在程序集。因此,在源文件开头,引入多个命名空间,并非加载多个程序集,不会造成“过度引用”的弊端。
命名空间分为两类:用户定义的命名空间和系统定义的命名空间。用户定义的命名空间是在代码中定义的命名空间。
using System.Data; using System.Data.Sqlclient;//用于操作SQL数据库
二、为命名空间或类型创建别名
创建 using 别名,以便更易于将标识符限定到命名空间或类型。
例子:新建一个控制台应用程序train_console,在.cs中添加如下代码:
namespace People { namespace Sleeping { public class Sleep { public Sleep() { Console.WriteLine("创建睡觉类"); } public void SleepAll() { Console.WriteLine("人都要睡觉的"); } } } }
在namespace trian_console中添加using引用的别名:
using sleep = People.Sleeping;//为命名空间使用using别名
在Main函数中添加:
sleep.Sleep s = new sleep.Sleep(); s.SleepAll();
运行结果如下:
三、强制资源清理
C# 通过 .NET Framework 公共语言运行库 (CLR) 中的GC(Garbage Collection)自动释放用于存储不再需要的对象的内存。内存的释放具有不确定性;一旦 CLR 决定执行垃圾回收,就会释放内存。
因为非托管资源不受GC控制,对象必须调用自己的Dispose()方法来释放,这就是所谓的Dispose模式。所以,通常最好尽快释放诸如文件句柄和网络连接这样的有限资源。
using 语句允许程序员指定使用资源的对象应当何时释放资源。为 using 语句提供的对象必须实现IDisposable接口。此接口提供了Dispose()方法,该方法将释放此对象的资源。
下面的示例显示用户定义类可以如何实现它自己的 Dispose 行为。注意类型必须从IDisposable继承。
新建一个控制台应用程序,添加一个类C(用于测试using释放资源的过程):
class C : IDisposable { public void UseLimitResource() { Console.WriteLine("使用有限的资源"); } void IDisposable.Dispose() { Console.WriteLine("释放有限的资源"); } }
在Main中添加:
Console.WriteLine("使用using释放资源:"); using (C c = new C()) //释放资源 { c.UseLimitResource(); } Console.WriteLine("资源成功释放!");
运行结果如下:
【规则】
- using只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口的类型使用using语句,否则会出现编译时错误;
- using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finnaly来实现,因为嵌套的using语句可能存在隐藏的Bug。内层using块引发异常时,将不能释放外层using块的对象资源。
- using语句支持初始化多个变量,但前提是这些变量的类型必须相同
using和try-catch-finally的使用异同
try-catch-finally 一起使用的常见方式是:在try块中获取并使用资源,在catch块中处理异常情况,并在finally块中释放资源。
共同点:都可以释放资源
不同点:
try-catch-finally可以用来捕获异常并处理,using不行;
using可以创建别名,导入命名空间 ,try-catch-finally不行;
using会在资源超出范围后主动释放对象,try-catch-finally要程序员自己写释放对象的代码
两者在处理操作数据库时的使用
很明显,Dispose方法是一个外部方法,系统并不会帮你调用。为了尽早释放对象所占用的资源,所以需要保证Dispose方法能尽早被执行。那么在.Net中提供了一个比较简便的方法,就是对于实现了IDisposable接口的类型对象提供了using语句。
对于操作数据库,使用using语句可以如下:
using( SqlConnection sqlConn = new SqlConnection( yourConnectionString ) ) { sqlConn.Open();//Open connection //Operate DB here using "sqlConn" sqlConn.Close();//Close connection }
但是有时候当多个对象需要释放的时候,例如:
SqlConnection sqlConn = new SqlConnection( yourConnectionString ); SqlCommand sqlComm = new SqlCommand( yourQueryString, sqlConn ); using(sqlConn as IDisposable) using(sqlComm as IDisposable) { sqlConn.Open();//Open connection sqlComm.ExecuteNonQuery();//Operate DB here }
这时候要特别注意,需要确保在发生异常的情况下,所有对象都能正常释放。显然,这段程序块,当构造“sqlComm”对象发生异常,会造成“sqlConn”对象无法及时被释放。虽说构造函数内部很少发生异常,或者说编写程序的时候要尽量避免从构造函数内向外散发异常。不过这里所说的意思是,要尽量把需要释放的对象放到using或者try-catch程序块,并作局部的异常处理,避免异常造成有些对象没有被释放。那么改进的方法,例如可以如下。
using( SqlConnection sqlConn = new SqlConnection( yourConnectionString ) ) using( SqlCommand sqlComm = new SqlCommand( yourQueryString, sqlConn ) ) { try { sqlConn.Open();//Open connection sqlComm.ExecuteNonQuery();//Operate DB here } catch( SqlException err ) { MessageBox.Show( err.Message ); } catch( Exception err ) { MessageBox.Show( err.Message ); } }
(如上,即使在构造“sqlComm”出现异常,也会释放“sqlConn”对象。)
但是对于using程序块来说,它有两个限制。
第一个就是using所对应的对象必须继承IDisposable,如果此对象没有继承IDisposable接口的话,系统会提示编译错误。
例如:
using( string strMsg = "My Test" ) Debug.WriteLine( strMsg );//Can't be compiled
第二个using对象检查是静态类型检查,并不支持运行时类型检查,因此如下形式也会出现编译错误。
SqlConnection sqlConn = new SqlConnection( yourConnectionString ); object objConn = sqlConn; using ( objConn ) { Debug.WriteLine( objConn.ToString() );//Can't be compiled }
不过对于后者,可以通过“as”来进行类型转换方式来改进。
SqlConnection sqlConn = new SqlConnection( yourConnectionString ); object objConn = sqlConn; using ( objConn as IDisposable ) { Debug.WriteLine( objConn.ToString() ); }
对于Dispose函数的调用来说,使用using程序块来完成只是最常用的方法,除此外,还可以用try-finally来完成,例如:
SqlConnection sqlConn = new SqlConnection( yourConnectionString ); try { sqlConn.Open(); //Operate DB here using "sqlConn" } finally { if( sqlConn != null ) sqlConn.Dispose(); }
完整例子如下:
新建一个控制台应用程序,再新建一个类using_sql,代码如下:
class using_sql { private static string constr = "Data Source=.;Initial Catalog=Csharp;Persist Security Info=True;User ID=sa;Password=huang_bx"; public static void SqlFun() { Console.WriteLine("使用using方法"); using (SqlConnection conn = new SqlConnection(constr)) { conn.Open(); string cmdstr = "select * from users"; SqlCommand cmd = null; try { cmd = new SqlCommand(cmdstr, conn); SqlDataReader sdr = cmd.ExecuteReader(); while (sdr.Read()) { Console.WriteLine("学号:{0} 姓名:{1}", sdr["UserID"].ToString().Trim(), sdr["UserName"].ToString().Trim()); } Console.WriteLine("Connection还未关闭!"); } catch (Exception ex) { Console.WriteLine("command出现异常:{0}", ex.Message.ToString()); } finally { cmd.Dispose(); } Console.WriteLine("Connection已经关闭!"); Console.WriteLine(); Console.WriteLine(); } } public static void sqltrycatch() { Console.WriteLine("使用try-catch-finally"); //使用try-catch-finally SqlCommand cmd1 = null; SqlConnection conn1 = null; try { conn1 = new SqlConnection(constr); conn1.Open(); string cmdstr = "select * from users"; cmd1 = new SqlCommand(cmdstr, conn1); SqlDataReader sdr = cmd1.ExecuteReader(); while (sdr.Read()) { Console.WriteLine("学号:{0} 姓名:{1}", sdr["UserID"].ToString().Trim(), sdr["UserName"].ToString().Trim()); } } catch (Exception e) { Console.WriteLine("command出现异常:{0}", e.Message.ToString()); } finally { cmd1.Dispose(); conn1.Dispose(); } } }
在Main函数中添加:
//using 在 操作数据库时的使用 Console.WriteLine("using 在操作数据库时的使用:"); using_sql.SqlFun(); using_sql.sqltrycatch();
运行结果如下:
为了及早释放对象所占用的非托管资源,因此要保证Dispose方法能在使用完对象后被调用,.Net提供了using程序块和try-finally程序块两个方法,两者并没有太大的区别,可能使用using能使程序看得比较简明,但是为了防止异常,需要在using中加入try-catch,这样反而不如try-finally看得舒服,不过这也只是我个人的看法。总的来说,只要把方法合理整合到应用程序当中才是最重要的。
参考资料:百度百科
http://www.cnblogs.com/falla/archive/2010/02/01/1660832.html