如果在方法调用期间实例化实现IDisposable的对象会发生什么?
例如
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;
}
答案 0 :(得分:3)
Dispose()
是一种常规方法
与任何其他方法一样,除非您编写调用它的代码,否则不会调用它。
例如,using()
语句生成调用Dispose()
方法的代码。
请注意,拥有原生资源的类应该有一个调用Dispose(false)
的终结器来释放它们(请参阅Dispose()
pattern)。
一旦对象是GC'd(可能是never happen)
答案 1 :(得分:0)
默认不是。这是一个示范性样本。你会看到猫从未被处置过。
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");
}
}
答案 2 :(得分:0)
除非MyMethod
调用其参数的Dispose()
方法,否则不会。它并不是一个伟大的模式。让拥有资源的代码处理资源。您的代码应更具惯用性地写为:
using (var o = new MyIDisposableObject())
{
return MyMethod(o);
}
答案 3 :(得分:0)
与C ++不同,C ++中的对象的生命周期和资源的清理紧密相连,在.NET中,它们基本上是分离的。只要系统“知道”它们,.NET中的对象就会存在,或者对它们的引用保存在系统知道的其他对象中。当对象的最后一次引用被覆盖时,对象不再存在。虽然系统对某些对象保持“隐藏”引用(例如,它具有已注册Finalize
方法的所有对象的列表),但在许多情况下,某些特定对象存在的唯一证据将是用户代码引用到那个对象。如果有例如GC知道的任何内容都没有使用1024字节的内存范围,GC既不知道也不关心16个64字节对象,16个字节对象和16字节对象是否保留了该空间,或其他一些对象组合。
这种方法非常适合管理内存。当对象要求其他实体执行操作(例如授予对文件的独占访问权限)时,问题就出现了,直到另行通知为止。如果一个要求对文件进行独占访问的对象就不再存在而不让任何人知道不再需要这样的访问,那么该文件将被其他人不必要地访问。 IDisposable
接口在某种程度上解决了这个问题:调用Dispose
方法的对象应该通知每个要求代表它做任何事情的实体,直到另行通知,它不再需要这样的服务。
正确使用IDisposable
的关键是确保每个需要清理的对象在任何给定时间都只有一个“所有者”。该所有者应该是通过using
或try
/ finally
块保护的局部变量,或者是实现IDisposable
的对象的字段,并且{{1}调用自己的Dispose
方法时的字段。如果具有拥有Dispose
的局部变量的方法在没有调用IDisposable
的情况下返回该变量,则所有权将被转移到方法的调用者。请注意,C#没有语言结构来识别所有权,除非在方法返回后不需要在方法中创建的对象(Dispose
块很好地处理该情况)。否则,程序员必须手动跟踪对象所有权。如果不这样做,将不会导致编译错误,但通常会导致程序无法正常工作,或者让外部实体不必要地等待通知他们的服务不再需要。
using
的情况并不完全符合上述任何一种模式,但仍然可以接受,因为如果它被一个try / finally保护,它看起来像:
return new MyIDisposableObject();
bool ok = false;
MyIDisposableObject ret = null;
try
{
ret = new MyIDisposableObject();
ok = true;
return ret;
}
finally
{
if (!ok && ret != null)
ret.Dispose();
}
语句可以执行的唯一方法是,在商店与ret.Dispose()
之间以及随后的返回之间发生异常。如果代码写为ret
,那么就不会发生异常。
但是,您的代码不同,因为您添加了另一个函数调用。但是,如果return new 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
块代码将知道它仅包含对finally
的引用,该引用即将消失,因此没有其他代码将使用innerObject
。因此,innerObject
块需要让finally
立即通知外部实体,不再需要他们的服务;如果不这样做,别无其他。