非托管资源、IDisposable 和自定义类型
Posted
技术标签:
【中文标题】非托管资源、IDisposable 和自定义类型【英文标题】:Unmanaged Resources, IDisposable and Custom Types 【发布时间】:2012-05-05 01:51:38 【问题描述】:关于这个主题的另一个主题,因为我厌倦了阅读无数主题以找到我的问题的答案:)
假设我们有以下类:
public class MyClass
private const string conString = "connection string";
private int Operation()
int count = 0;
using(var con = SqlConnection(conString))
string select_cmd = "SELECT * FROM TABLE";
using(var cmd = new SqlCommand(select_cmd, con))
using(var reader = cmd.ExecuteReader())
while(reader != null && reader.Read())
count++;
return count;
由于与数据库的连接是在 using 语句中实例化的,因此 con.close() 和最终 con.dispose() 方法将被调用,是否需要为 MyClass 实现 IDisposable? MyClass 超出范围时会被垃圾收集吗?
编辑:
感谢您的回复,这就是我的想法,但我需要说清楚。还有一个问题。
如果我的类有几个操作()在数据库上做一些工作,从资源消耗的角度来看,拥有一个 SqlConnection 成员,实例化并在类构造函数上打开它并实现 IDisposable 以关闭它是更好的做法吗?它而不是在每个操作中使用“使用”语句(在每个操作上打开和关闭数据库)? 当然那样我应该只在 using 语句中实例化和使用 MyClass 对象。
【问题讨论】:
【参考方案1】:不,如果您的类在 Operation() 方法之外持有 SqlConnection 实例,那么您只需要实现 IDisposable,这样它就可以与类本身保持活动状态(例如,如果您将它分配给一个类成员字段或属性)。
在您的类超出范围之前,SqlConnection 实例将被标记为可以清理,因为该实例超出了 using 块中的范围。最重要的是,非托管数据库连接(由 SqlConnection 封装)已通过 Dispose 调用释放。当 GC 感到有足够的压力证明进行 GC 通过时,SqlConnection 的托管部分将被释放。
至于您的类实例,它也将由 GC 自行决定释放,这是您无需担心的事情,除非您在整个生命周期内创建和销毁您的类的大量实例你的应用程序(我想数百万)。
编辑
对于您的第二个问题,最好使用和处置每个方法的 SqlConnection 实例,因为这个特定的数据库客户端类在内部实现了连接池(即:当您处置它一段时间时,它并没有真正关闭连接,因此当创建新的 SqlConnection 时,它会重用打开连接池中的一个)。
请参阅(旧但有效):http://msdn.microsoft.com/en-us/library/8xx3tyca%28v=vs.71%29.aspx
如果您使用的数据库客户端 API 没有实现自己的池,最好按照您的建议手动管理它。但是,您必须小心同步对连接的访问(即:不要让 2 个线程同时使用它)并自己管理生命周期问题(例如:如果您的类在应用程序的所有持续时间内都保持活动状态,那么您将无限期打开数据库资源...等等)
大多数现代的(mysql Connector.NET、SQL Server)都实现了池化。
【讨论】:
感谢您的详细解答,非常感谢!【参考方案2】:Operation()
完成后,该方法中使用的所有非托管资源都将被释放。它没有理由实现IDisposable
。 MyClass
不会打开任何非托管资源,因此它不需要实现该接口。
关于您的第二个问题,您的类的实例将像任何其他托管对象一样被垃圾收集。
【讨论】:
【参考方案3】:不,不需要实现 IDisposable 接口,你没有资源可以释放。
【讨论】:
以上是关于非托管资源、IDisposable 和自定义类型的主要内容,如果未能解决你的问题,请参考以下文章
编写高质量代码改善C#程序的157个建议——建议46:显式释放资源需继承接口IDisposable