与 C++ 不同,在 C++ 中,对象的生命周期与其资源的清理密切相关,而在 .NET 中,它们在很大程度上是分离的。只要系统“知道”它们,.NET 中的对象就会存在,或者对它们的引用保存在系统知道的其他对象中。当对它们的最后一个引用被覆盖时,对象将不复存在。尽管系统保持对某些对象的“隐藏”引用(例如,它具有所有已注册对象的列表Finalize
方法),在许多情况下,某个特定对象曾经存在的唯一证据将是对该对象的用户代码引用。例如,如果有一个 1024 字节范围的内存没有被 GC 知道的任何东西使用,那么 GC 将不知道也不关心该空间是否已被 16 个 64 字节对象、12 个 84 字节对象和一个16 字节对象,或其他一些对象组合。
这种方法非常适合管理内存。当对象要求其他实体做某事(如授予对文件的独占访问权)直到另行通知时,它的一个问题就出现了。如果请求对文件的独占访问权的对象只是不存在而没有让任何人知道不再需要这种访问权,那么其他人将不必要地无法访问该文件。该IDisposable
接口在一定程度上解决了这个问题:调用其Dispose
方法的对象应该通知每个请求代表它做任何事情的实体,直到另行通知,它不再需要此类服务。
正确使用的关键IDisposable
是确保每个需要清理的对象在任何给定时间都只有一个“所有者”。该所有者应该是通过using
或try
/finally
块保护的局部变量,或者是实现的对象的字段,并且在调用其自己的方法时IDisposable
将成为该字段。如果具有局部变量的方法在没有调用的情况下返回该变量,则所有权将转移给方法的调用者。请注意,C# 没有识别所有权的语言结构,除非在方法返回后不再需要在方法中创建的对象(aDispose
Dispose
IDisposable
Dispose
using
块很好地处理这种情况)。否则,程序员必须手动跟踪对象所有权。不这样做不会导致编译错误,但通常会导致程序无法工作,或者让外部实体不必要地等待通知不再需要他们的服务。
的情况return new MyIDisposableObject();
并不完全符合上述任何一种模式,但仍然可以接受,因为如果它由 try/finally 保护,它看起来像:
bool ok = false;
MyIDisposableObject ret = null;
try
{
ret = new MyIDisposableObject();
ok = true;
return ret;
}
finally
{
if (!ok && ret != null)
ret.Dispose();
}
该语句可以执行的唯一方法是在 store to和以下 returnret.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
区块需要innerObject
立即通知外部实体不再需要他们的服务;,如果它不这样做,其他任何东西都不会。