1

如果在方法调用期间实例化一个实现 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;
    }
4

4 回答 4

3

Dispose()是一种正常的方法。
像任何其他方法一样,除非您编写调用它的代码,否则它不会被调用。

例如,该using()语句生成调用该Dispose()方法的代码。

请注意,拥有本机资源的类应该有一个调用Dispose(false)释放它们的终结器(参见Dispose()模式)。
一旦对象被 GC 处理,终结器就会运行(这可能永远不会发生

于 2013-10-11T20:37:28.150 回答
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");
    }
}
于 2013-10-11T20:52:26.407 回答
0

除非MyMethod调用Dispose()其参数的方法,否则不会。这不是一个很好的模式。让拥有资源的代码配置资源。您的代码应该更惯用地写为:

using (var o = new MyIDisposableObject())
{
    return MyMethod(o);
}
于 2013-10-11T21:25:54.397 回答
0

与 C++ 不同,在 C++ 中,对象的生命周期与其资源的清理密切相关,而在 .NET 中,它们在很大程度上是分离的。只要系统“知道”它们,.NET 中的对象就会存在,或者对它们的引用保存在系统知道的其他对象中。当对它们的最后一个引用被覆盖时,对象将不复存在。尽管系统保持对某些对象的“隐藏”引用(例如,它具有所有已注册对象的列表Finalize方法),在许多情况下,某个特定对象曾经存在的唯一证据将是对该对象的用户代码引用。例如,如果有一个 1024 字节范围的内存没有被 GC 知道的任何东西使用,那么 GC 将不知道也不关心该空间是否已被 16 个 64 字节对象、12 个 84 字节对象和一个16 字节对象,或其他一些对象组合。

这种方法非常适合管理内存。当对象要求其他实体做某事(如授予对文件的独占访问权)直到另行通知时,它的一个问题就出现了。如果请求对文件的独占访问权的对象只是不存在而没有让任何人知道不再需要这种访问权,那么其他人将不必要地无法访问该文件。该IDisposable接口在一定程度上解决了这个问题:调用其Dispose方法的对象应该通知每个请求代表它做任何事情的实体,直到另行通知,它不再需要此类服务。

正确使用的关键IDisposable是确保每个需要清理的对象在任何给定时间都只有一个“所有者”。该所有者应该是通过usingtry/finally块保护的局部变量,或者是实现的对象的字段,并且在调用其自己的方法时IDisposable将成为该字段。如果具有局部变量的方法在没有调用的情况下返回该变量,则所有权将转移给方法的调用者。请注意,C# 没有识别所有权的语言结构,除非在方法返回后不再需要在方法中创建的对象(aDisposeDisposeIDisposableDisposeusing块很好地处理这种情况)。否则,程序员必须手动跟踪对象所有权。不这样做不会导致编译错误,但通常会导致程序无法工作,或者让外部实体不必要地等待通知不再需要他们的服务。

的情况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立即通知外部实体不再需要他们的服务;,如果它不这样做,其他任何东西都不会。

于 2013-10-13T16:20:49.990 回答