引用 using() 块之外的对象

Referencing an object outside of using() block

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 时,validationTable 已被处理但未被垃圾收集——但仍应在 .ToDictionary() 调用中抛出 ObjectDisposedException。但是这段代码愉快地构建了一个有效的字典并继续运行。

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

我的问题:

  1. 此代码是否有效且行为正常?
  2. 如果不是,那么我们现在看到的成功只是胡扯吗?
  3. 是否有关于 DataTable 的特定内容允许在对象被释放后访问 - 类似于 GZipStream class 要求您释放对象以刷新对象的方式流,因此允许在处理对象后调用 .ToArray().GetBuffer()?
  4. ... 调用方法时究竟是什么导致抛出 ObjectDisposedException?我一直认为这是来自 .NET 框架本身。

.

澄清:

这是一个 .NET Framework 问题。共识是我的理解是正确的——DataTable 本身必须抛出 ObjectDisposedException。除了它没有。 DataTable 源代码中没有任何地方-因此我问。我假设框架在处理后会确保 ObjectDisposedException,但显然不是这种情况……与 GZipStream 不同,它只允许访问 Dispose() 之后的两个方法,即 DataTable DGAF。美好的。

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

此外,如果您对问题投反对票或投票关闭它,请留下评论原因。

根据 documentation on using:

As a rule, when you use an IDisposable object, you should declare and instantiate it in a using statement. The using statement calls the Dispose method on the object in the correct way, and (when you use it as shown earlier) it also causes the object itself to go out of scope as soon as Dispose is called. Within the using block, the object is read-only and cannot be modified or reassigned.

  1. 是的。请参阅下面的引用。
  2. N/A
  3. N/A。如果您查看 IDisposable.Dispose(),它指出 "Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources." 如果可以在没有托管资源的情况下提供功能,则不需要处置对象来阻止对该功能的访问。
  4. class 的开发者实现了您正在访问的方法或 属性 添加了代码来检测对象是否被释放并根据需要抛出异常。

也来自 documentation on using:

You can instantiate the resource object and then pass the variable to the using statement, but this is not a best practice. In this case, the object remains in scope after control leaves the using block even though it will probably no longer have access to its unmanaged resources. In other words, it will no longer be fully initialized. If you try to use the object outside the using block, you risk causing an exception to be thrown. For this reason, it is generally better to instantiate the object in the using statement and limit its scope to the using block.

简而言之,validationTable 已被处置,无法再访问其非托管资源,但托管资源(数据的本地副本)仍然可用。这是假设 ValidationDataTable 已正确实施。由于我没有通过 google 或 msdn 找到它,我假设它是一个内部的 class,所以一切顺利。

正如 Lee 在他的评论中指出的那样,DataTable 是一次性的,因为它继承自 MarshalByValueComponent。意外的是 Dispose() 没有做任何会导致稍后抛出 Disposed 异常的事情。好吧,这本身并不是真正的 事故 ,但是没有什么可以阻止框架的更高版本执行某些 确实 导致该异常的事情.

我认为依靠它是个坏主意,我会将使用 DataTable 的代码移到包装它的 using 中。

我认为您遗漏的部分是 "disposing" 除了程序员在 IDisposable.Dispose 实现中定义的内容之外,对象不做任何事情。除了为 using 语句提供支持外,该语言或框架没有做任何特殊的事情。

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

什么会引发 ObjectDisposedException?好吧,实现 IDisposable 类型的程序员需要在某处编写这样的代码:

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

所以在你的情况下,如果 ValidationDataTable 的实现方式不跟踪它是否被处置,并且它将它的数据存储在内存中,那么它就会像通常。语言或框架并不能阻止这种情况发生。


更新:为了回答评论,看起来 DataTable 没有直接实现 IDisposable 但它是 class (MarshalByValueComponent) 的基础。为了支持 WinForms 设计器体验,他们必须从那个基础 class 继承。在设计模式之外,Dispose 不会改变任何东西。所以你可以安全地忽略它来正常使用。换句话说,您不需要在 using 块中使用它。

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