C# IDisposable在方法调用中实例化

C# IDisposable在方法调用中实例化,c#,idisposable,C#,Idisposable,如果在方法调用期间实例化一个实现IDisposable的对象,会发生什么 比如说 return MyMethod(new MyIDisposableObject()); MYIDisposableObject的Dispose方法是否会被调用 好的,那么如果我在MyIDisposableObject中有以下代码,IDBConnection会被关闭并正确处理吗,还是仍然不安全 protected IDbConnection _db; bool _disposed; public

如果在方法调用期间实例化一个实现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)
来释放它们(请参阅)。

一旦对象是GC,终结器将运行(默认情况下可能不是这样)。这是一个示例。你会发现这只猫从来没有被处置过

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中的对象,或者系统知道的其他对象中保存了对它们的引用,那么.NET中的对象就是有效的。对象的最后一个引用被覆盖时,对象将不再存在。尽管系统保留对某些对象的“隐藏”引用(例如,它有一个已注册

Finalize
方法的所有对象的列表),但在许多情况下,某些特定对象曾经存在的唯一证据将是该对象的用户代码引用。例如,如果存在一个1024字节的内存范围,而GC所知道的任何东西都不使用该范围,则GC既不知道也不关心该空间是否由16个64字节对象、12个84字节对象和一个16字节对象或其他一些对象的组合持有

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

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

返回新的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
和以下返回之间的某个时间发生异常。如果代码写为
返回新的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
变量将不会被清除,
最后
块代码将因此知道它持有对
innerObject
的唯一引用,该引用即将消失,因此任何其他代码都不会使用
innerObject
。因此,
finally
块需要请求
innerObject
立即通知外部实体不再需要它们的服务,如果有