引用using()块之外的对象

本文关键字:对象 using 引用 | 更新日期: 2024-10-20 05:09:48

关于SO的这个主题有很多问题,但我还没有找到一个涵盖我特别需要理解的内容的问题。

我的一个开发人员写了这样的代码:

//
    //  ValidationDataTable is a typed DataTable, generated by the Framework
    ValidationDataTable  validationTable;
    using (ValidationTableAdapter adapter = new ValidationTableAdapter ()) {
        using (validationTable = adapter.GetData()) { }
    }
    datafeedValidators.Add(new CountryFieldValidator(validationTable.ToDictionary(key => key.CountryCode, value => value.CountryName)));
    //  Party on...
//

我的理解是:当validationTable在最后一行代码中被引用时,它已经被丢弃,但没有被垃圾收集——但它仍然应该在.ToDictionary()调用上抛出一个ObjectDisposedException。但这段代码愉快地构建了一个有效的字典并继续运行。

我有一些理论,但找不到任何确凿的证据来证实或推翻其中的任何一个。并且可以通过十几种方式重写代码来避免这个问题;这不是问题。我只需要知道我的理解差距是什么

我的问题:

  1. 此代码是否有效,是否正常运行
  2. 如果不是,我们看到的成功只是一场掷骰子游戏吗
  3. DataTable是否有特定的东西允许在对象被释放后进行访问——类似于GZipStream类要求您释放对象以刷新流,从而允许在对象释放后调用.ToArray().GetBuffer()的方式
  4. 。。。在调用方法时,究竟是什么导致引发ObjectDisposedException?我一直认为这是来自.NET框架本身

澄清:

这是一个.NET Framework问题。大家一致认为,我的理解是正确的——DataTable本身必须抛出ObjectDisposedException。但事实并非如此。在DataTable源代码中没有任何位置——因此我提出了这个问题。我假设该框架在处理完ObjectDisposedException后会确保它,但事实显然并非如此。。。与GZipStream不同,GZipStream只允许访问Dispose()之后的两个方法,即DataTable DGAF。好的

因此,让我重新表述一下这个问题:DataTable内部是否有任何东西会轰炸我们,因为允许调用已处理的表?我可以假设微软内部没有清理任何东西,只要对象在范围内,所有属性和值都会在那里,不受影响——这似乎不是一个安全的假设。不管怎样,这个代码都会消失——我只是想了解微软是否有故意的原因允许在Dispose()之后访问DataTable,或者这是疏忽,不在乎,等等。

此外,如果你对一个问题投了反对票或投票关闭它,请留下评论为什么。

引用using()块之外的对象

我认为您缺少的部分是,"处理"一个对象除了程序员在IDisposable.Dispose实现中定义的之外,什么都不做。该语言或框架除了提供对using语句的支持之外,没有做任何特殊的事情。

使用using语句,该语言只提供以下内容:如果您的对象实现了这个名为IDisposable的特定接口,那么它承诺在using块存在时调用Dispose方法。就是这样。它不知道哪些物体被"处理"了。它不会通过特殊方式跟踪已处理的对象来抛出ObjectDisposedException

什么抛出ObjectDisposedException?实现IDisposable类型的程序员需要在其中的某个地方编写这样的代码:

void DoMoreWork()
{
    if(_iHaveBeenDisposedAlready) 
        throw new ObjectDisposedException(null);
    ...

因此,在您的情况下,如果ValidationDataTable的实现方式不跟踪它是否被处理,并且它将数据存储在内存中,那么它将照常工作。语言或框架并不能阻止这种情况的发生。


UPDATE:为了回答评论,看起来DataTable没有直接实现IDisposable,但它的基类(MarshalByValueComponent)实现了。为了支持WinForms设计器体验,它们必须继承该基类。在设计模式之外,Dispose不会发生任何变化。因此,您可以放心地忽略它以供正常使用。换句话说,您不需要在using块中使用它。

这正常吗?不会。通常,IDisposable对象是指在其正常生命周期的某个地方进行处理。拥有一个不需要处理的IDisposable肯定是令人困惑的。

正如Lee在评论中指出的,DataTable是可丢弃的,因为它继承了MarshalByValueComponent。Dispose()没有执行任何会导致稍后抛出Disposed异常的操作,这是一个意外。嗯,这本身并不是一个事故,但没有什么可以阻止框架的后续版本做导致该异常的事情。

我认为指望这一点是个坏主意,我会将使用DataTable的代码移动到包装它的using中。

根据using:上的文档

通常,当您使用IDisposable对象时,您应该在using语句中声明并实例化它。using语句以正确的方式调用对象上的Dispose方法,并且(如前所示使用它时)一旦调用Dispose,它还会导致对象本身超出作用域。在使用块中,对象是只读的,不能修改或重新指定。

  1. 是的。请参阅下面的报价
  2. 不适用
  3. 不适用。如果查看IDisposable.Dispose(),它表示"执行与释放、释放或重置非托管资源相关的应用程序定义任务。"如果可以在没有托管资源的情况下提供功能,则不需要处理对象来阻止对该功能的访问
  4. 实现您正在访问的方法或属性的类的开发人员会添加代码来检测对象是否已被释放,并根据需要抛出异常

同样来自using:上的文档

您可以实例化资源对象,然后将变量传递给using语句,但这不是最佳做法。在这种情况下,在控件离开using块后,即使对象可能不再具有对其非托管资源的访问权限,该对象仍保持在作用域中。换句话说,它将不再被完全初始化。如果您试图在using块之外使用对象,则有引发异常的风险。出于这个原因,通常最好在using语句中实例化对象,并将其范围限制在using块。

简而言之,validationTable已被释放,不再可以访问其非托管资源,但托管资源(数据的本地副本)仍然可用。这是假设ValidationDataTable被正确地实现。由于我没有通过谷歌或msdn找到它,我假设它是一个内部类,所以一切都会发生。