C# IDisposable在方法调用中实例化
如果在方法调用期间实例化一个实现IDisposable的对象,会发生什么 比如说C# IDisposable在方法调用中实例化,c#,idisposable,C#,Idisposable,如果在方法调用期间实例化一个实现IDisposable的对象,会发生什么 比如说 return MyMethod(new MyIDisposableObject()); MYIDisposableObject的Dispose方法是否会被调用 好的,那么如果我在MyIDisposableObject中有以下代码,IDBConnection会被关闭并正确处理吗,还是仍然不安全 protected IDbConnection _db; bool _disposed; public
return MyMethod(new MyIDisposableObject());
MYIDisposableObject的Dispose方法是否会被调用
好的,那么如果我在MyIDisposableObject中有以下代码,IDBConnection会被关闭并正确处理吗,还是仍然不安全
protected IDbConnection _db;
bool _disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyIDisposableObject()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// free other managed objects that implement
// IDisposable only
if (_db != null)
{
_db.Close();
_db.Dispose();
}
}
// release any unmanaged objects
// set the object references to null
_db = null;
_disposed = true;
}
Dispose()
是一种正常的方法。与任何其他方法一样,除非编写调用它的代码,否则不会调用它 例如,
using()
语句生成调用Dispose()
方法的代码
请注意,拥有本机资源的类应该有一个终结器,该终结器调用Dispose(false)
来释放它们(请参阅)。一旦对象是GC,终结器将运行(默认情况下可能不是这样)。这是一个示例。你会发现这只猫从来没有被处置过
class Program
{
public static void Main(string[] args)
{
SayCatName(new Cat() { Name = "Whiskers" });
Console.Read();
}
public static void SayCatName(Cat c)
{
Console.WriteLine(c.Name);
}
}
public class Cat : IDisposable
{
public string Name { get; set; }
public void Dispose()
{
Console.WriteLine("Cat was disposed");
}
}
除非
MyMethod
调用其参数的Dispose()
方法,否则不会。这不是一个很好的模式。让拥有资源的代码来处置资源。您的代码应该更习惯地编写为:
using (var o = new MyIDisposableObject())
{
return MyMethod(o);
}
不同于C++,其中对象的生命周期和资源的清理是紧密相连的,在.NET中,它们大部分是独立的。只要系统“知道”关于.NET中的对象,或者系统知道的其他对象中保存了对它们的引用,那么.NET中的对象就是有效的。对象的最后一个引用被覆盖时,对象将不再存在。尽管系统保留对某些对象的“隐藏”引用(例如,它有一个已注册
Finalize
方法的所有对象的列表),但在许多情况下,某些特定对象曾经存在的唯一证据将是该对象的用户代码引用。例如,如果存在一个1024字节的内存范围,而GC所知道的任何东西都不使用该范围,则GC既不知道也不关心该空间是否由16个64字节对象、12个84字节对象和一个16字节对象或其他一些对象的组合持有
这种方法对于管理内存非常有效。当对象要求其他实体做一些事情(比如授予对文件的独占访问权)直到另行通知时,就会出现一个问题。如果请求以独占方式访问某个文件的对象不再存在,而不让任何人知道不再需要这种访问,那么其他人将不必要地无法访问该文件。IDisposable
接口在某种程度上解决了这个问题:调用了Dispose
方法的对象应该通知每个请求代表它做任何事情的实体,直到进一步通知为止,它不再需要这样的服务
正确使用IDisposable的关键是确保每个需要清理的对象在任何给定时间都只有一个“所有者”。该所有者应该是一个局部变量,通过使用
或try
/最终
块进行保护,或者是一个实现了IDisposable
的对象字段,当调用其自己的Dispose
方法时,该字段将Dispose
该字段。如果具有本地变量的方法拥有一个IDisposable
而没有调用Dispose
,则该方法的所有权将转移给该方法的调用方。请注意,C#没有语言结构来识别所有权,除非在方法返回后不需要在方法中创建对象(使用块可以很好地处理这种情况)。否则,程序员必须手动跟踪对象所有权。不这样做不会导致编译错误,但通常会导致程序无法运行,或者让外部实体不必要地等待不再需要它们的服务的通知
返回新的MyIDisposableObject()的情况
不完全符合上述任何一种模式,但仍然可以接受,因为如果它由try/finally保护,它看起来像:
bool ok = false;
MyIDisposableObject ret = null;
try
{
ret = new MyIDisposableObject();
ok = true;
return ret;
}
finally
{
if (!ok && ret != null)
ret.Dispose();
}
执行ret.Dispose()
语句的唯一方法是在存储到ret
和以下返回之间的某个时间发生异常。如果代码写为返回新的MyIDisposableObject()代码>,那里不可能发生异常
但是,您的代码是不同的,因为您添加了另一个函数调用。但是,只有当MyMethod
承诺返回封装传入对象的对象,或者Dispose
由于异常而无法返回时,该模式才是安全的。由于通常情况并非如此,根据MyMethod
是否应该返回封装传入引用的对象,正确的模式可能是:
using (var myObject = new MyDisposableObject())
return MyMethod(myObject);
如果MyDisposableObject
不会封装在MyMethod
返回的对象中,因此一旦返回就不再有任何用途,或者
MyIDisposableObject innerObject = new MyDisposableobject;
try
{
var ret = MyMethod(innerObject);
innerObject = null; // Note that `ret` still has an encapsulated reference to it
return ret;
}
finally
{
if (innerObject != null) // Reference wasn't yet encapsulated in `ret`
innerObject.Dispose();
}
请注意,如果对MyMethod
的调用成功,则innerObject
变量将被清除,但该对象不会被告知向外部实体发出停止其服务的通知,因为调用代码将需要MyMethod
返回的对象,而该对象反过来需要innerObject
,这反过来又需要这些外部实体的服务。如果对MyMethod
的调用引发异常,那么innerObject
变量将不会被清除,最后
块代码将因此知道它持有对innerObject
的唯一引用,该引用即将消失,因此任何其他代码都不会使用innerObject
。因此,finally
块需要请求innerObject
立即通知外部实体不再需要它们的服务,如果有