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;
    }

ID在方法调用中实例化

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的关键是确保每个需要清理的对象在任何给定时间都有一个"所有者"。 该所有者应该是通过usingtry/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立即通知外部实体不再需要其服务;,如果它不这样做,其他任何东西都不会。