ID在方法调用中实例化
本文关键字:实例化 调用 方法 ID | 更新日期: 2023-09-27 18:34:54
如果在方法调用期间实例化实现 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;
}
Dispose()
是一种正常方法。
与任何其他方法一样,除非您编写调用它的代码,否则不会调用它。
例如,using()
语句生成调用 Dispose()
方法的代码。
请注意,拥有本机资源的类应该有一个终结器,该终结器调用Dispose(false)
来释放它们(请参阅Dispose()
模式(。
终结器将在对象被 GC'd 后运行(这可能永远不会发生(
默认情况下不会。下面是一个演示性示例。你会看到猫从来没有被处理掉。
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 中的对象就会存在。 当对对象的最后一个引用被覆盖时,对象将不复存在。 尽管系统保留了对某些对象的"隐藏"引用(例如,它有一个已注册Finalize
方法的所有对象的列表(,但在许多情况下,某些特定对象曾经存在的唯一证据将是对该对象的用户代码引用。 例如,如果有一个 1024 字节的内存范围没有被 GC 知道的任何内容使用,GC 既不会知道也不关心该空间是否由 16 个 64 字节对象、十几个 84 字节对象和一个 16 字节对象或其他对象组合占据。
此方法非常适合管理内存。 当对象要求其他实体执行操作(例如授予对文件的独占访问权限(直到另行通知时,它的一个问题就出现了。 如果一个请求对文件进行独占访问的对象只是不复存在,而没有让任何人知道不再需要这种访问权限,那么其他人将不必要地无法访问该文件。 IDisposable
接口在某种程度上解决了这个问题:调用其Dispose
方法的对象应该通知每个要求代表它执行任何操作的实体,直到另行通知,它不再需要此类服务。
正确使用IDisposable
的关键是确保每个需要清理的对象在任何给定时间都有一个"所有者"。 该所有者应该是通过using
或try
/finally
块保护的局部变量,或者是实现IDisposable
的对象的字段,并且在调用自己的Dispose
方法时将Dispose
该字段。 如果一个具有局部变量的方法拥有IDisposable
返回该变量而没有调用Dispose
,那么所有权将转移给该方法的调用者。 请注意,C# 没有语言构造来识别所有权,除非在方法返回后不需要在方法中创建的对象(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();
}
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
区块需要要求innerObject
立即通知外部实体不再需要其服务;,如果它不这样做,其他任何东西都不会。