在
NET
x 的 C#
NET
的各种语言中
有所谓的 using statement (如本 blog 上一篇帖子「使用ADO
NET 的NextResult 方法取得多个Result Set」的代码范例)
可保证自动 dispose (释放) unmanaged object (对象) 所占用的资源
包括因未处理的 exception 而造成区块结束 (但 StackOverflowException 除外)
系统都会 dispose 资源
因此若您在 using 区块中建立了数据库的 connection
即无须再手动 close connection
亦无须再下 Connection
Dispose()
Command
Dispose() 等指令
根据 MSDN Library 和目前市面上几本 ADO
NET
原文书都有提到
在 using 区块中会自动去做 dispose 的动作
NET 的 garbage collector 会自动释放不再使用的 managed resources 所占用的内存
不用程序员手动撰码
但 unmanaged resources 则需要程序员自行下 Dispose() 去做处理
以让对象彻底终止 unmanaged resources 的使用
例如传统的做法
常会在 try
catch
finally pattern 中去呼叫 Dispose 方法
但若是数据库的联机
则必须有不同考虑
因为若任意下 Dispose 提早回收
也可能导致联机无法有效地被重复引用
现在大部分的数据库和 Data Provider 都有支持 Connection Pooling 机制亦即在建立完数据库的联机后当程序员呼叫 Close 方法关闭一个数据库的 Connection 对象时NET 的 Data Provider 并不会将这个对象所占用的内存空间释放掉而是将此对象暂存至 Pool 之中 以便待会可以再重复使用
若在设定时间 (默认为 秒) 内没有应用程序使用到此对象或是呼叫了 Dispose 方法则 NET Data Provider 才会真正关闭这个联机并由 Garbage Collector 自动将资源收回因此常有 web 程序员在网络上各讨论区提到是否有必要在呼叫 Close 方法后再呼叫 Dispose 方法并将 Connection 设为 Nothing (或 Null)?答案是不必要的因为 GC 过一阵子就会自动回收未再被参照的联机手动呼叫 Dispose 只不过提早回收的动作而已而且若是该联机可能会在短时间内被大量使用者同时存取的话也应让其待在 Pool 中待命而应避免手动呼叫 Dispose 方法导致它被真正关闭并被回收而无法有效地被重复使用
由于 GC 只会在系统闲置或内存不足时才启动因此除非是使用频率非常繁复的资源否则交由 GC 自行处理即可而设为 Nothing (或 Null) 也只是将 Connection 变量的位置清除 (Null Reference)事实上原来 New 出来的 Connection 对象还是存在而 Dispose 方法是用来处理自行建立的 Windows 资源但又不会自行释放的对象如档案 (开档与关文件)GDI 对象 (直接由 Win API 叫用)…等等
接下来再回头探讨 NET 的 VB/C# 语言中都有的 using statementusing 语句算是简易版的 tryfinally pattern可让程序员以较简便的写法尽早去释放资源尤其最适用在有限的 unmanaged resources 上例如档案和串流 I/OSocket 网络连接File Handle (档案控制代码)COM 组件绘图和字形数据库存取WorkflowRuntime (WF)…等等的内存自动释放using statement 遇到例外时也会抛出例外 (throw)但不会去 catch 处理例外因此若您想要自行处理例外的话只能回归传统的 trycatchfinally 写法
提供给 using statement 的对象必须实作 IDisposable 接口若是自己写的 class只要实作 SystemIDisposable 接口即拥有 Dispose 方法之后若引用 using statement 去释放这个 class 的 instance即会自动做 object 的 Close()Dispose()设定为 null (Nothing) 这三种动作不需要再自己手动处理反而若是自己手动多下一次 Close()会让 CLR 浪费资源多做一次处理反倒会影响程序「性能 (performance)」根据国外网站及 ADONET 书籍证实若 using 语句搭配 CommandBehaviorCloseConnection 一起使用其重复关闭数据库联机的动作会大幅地降低程序性能处理时间甚至会多出 % 以上 (叫用 ExecuteReader() 时若搭配 CommandBehavior 枚举值 (enumerated value)可要求在查询完成后自动关闭数据库联机)
此外using statement 也可多层巢状地使用例如第一层的 using statement 里包 SqlConnection 的宣告及 instance 的新增第二层包 SqlCommand第三层包 SqlDataReader您亦可在巢状的 using statement 中指定多种的系统资源包括数据库的 transaction 交易处理