在 using() 块之外引用对象

3

这个主题在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 Framework本身。
澄清:
这是一个.NET Framework问题。 共识是我的理解是正确的 - DataTable本身必须抛出ObjectDisposedException。 除了它没有。 不在DataTable源代码中 - 因此我问。 我假设框架将确保在处理后抛出ObjectDisposedException,显然不是这种情况......与GZipStream不同,它只允许在Dispose()之后访问两个方法,DataTable DGAF。 好的。
那么,让我重述问题:是否有任何内部的DataTable会因为允许调用已处理表而炸弹我们? 我可以假设Microsoft在内部没有清理任何内容,所有属性和值将保持原样,在对象在范围内的时间内未经触摸......这似乎不是一个安全的假设。不管怎样,这段代码都将消失 - 我只是想知道Microsoft允许在Dispose()后访问DataTable是否有故意的原因或是疏忽,不关心等等。
此外,如果您对问题进行了downvote或投票关闭,请留下评论说明原因。

4
如果此代码让团队成员产生疑惑并且不容易理解(我同意这种感受),那么为了团队的可支持性,它应该被改变。那个开发者应该更加努力地提高可支持性,而不是只注重聪明才智。完全空的代码块应该引起任何编写代码的人的警惕。 - David
是的,确实如此。不幸的是,我不确定这是否只是开发人员理解上的缺失,而不是聪明才智。也可能只是一个疲惫的错误 :P - James King
2
同意@David的观点。通常,空块意味着你做错了。简短回答:1. 可能,2. 可能不,3. 是的 - DataTable 在被处理时实际上并没有任何事情。这是一些争议的来源,但要明确的是,处理一个 DataTable 不会有任何影响。4. 这取决于特定类的实现。框架和语言都不关心处理。我建议您阅读可处理模式的相关内容。 - Glorin Oakenfoot
2
DataTableDispose 实现是从其父类继承的,因此它不一定在 DataTable 中执行任何操作。类在处置后访问时并不总是会抛出 ObjectDisposedException 异常,比如 MemoryStream - Lee
1
鉴于您不愿接受Eren Ersönmez的答案,这个问题已经进入了要求我们读取.Net Framework开发人员思维的领域,可能应该被关闭。 - OldFart
显示剩余4条评论
3个回答

6
我认为你所缺失的部分是,“处理”一个对象除了程序员在实现中定义的行为外,不会有其他任何特殊操作。 语言或框架并没有做任何特殊的事情,只提供语句的支持。
使用语句时,语言只提供以下内容:如果您的对象实现了这个名为的特定接口,那么它承诺在退出块时调用方法。就是这样。 它不知道哪些对象已被“处理”,也不会通过以特殊方式跟踪已处理的对象来引发异常。
什么会引发?实现类型的程序员需要在其中某个位置编写以下代码:
void DoMoreWork()
{
    if(_iHaveBeenDisposedAlready) 
        throw new ObjectDisposedException(null);
    ...

因此,在您的情况下,如果ValidationDataTable的实现方式没有跟踪它是否被处理,并且将其数据存储在内存中,则它将像往常一样工作。语言或框架不会阻止这种情况发生。
更新:回答评论,看起来DataTable并没有直接实现IDisposable,但是它的基类(MarshalByValueComponent)确实实现了IDisposable。他们必须继承该基类以支持WinForms设计体验。在设计模式之外,Dispose不会改变任何内容。因此,您可以安全地忽略它以供正常使用。换句话说,您不需要在using块内使用它。
这正常吗?不。通常,IDisposable对象应在其正常生命周期中的某个地方进行处理。拥有一个不需要处理的IDisposable肯定会令人困惑。

我澄清了代码,表明ValidationDataTable是由.NET框架生成的一个带类型的DataTable。我认为我表达问题的方式不太好 - 我正在尝试弄清楚为什么在Dispose之后框架会允许访问DataTable成员。如果不通过DataTable继承层次结构中的框架源代码进行挖掘,这样做就像在地雷区打高尔夫球一样危险。 - James King
另外,我们不会保留这段代码……我只是想了解DataTable背后发生了什么。这是否像GZipStream一样,访问是有意允许的?还是疏忽大意?或者微软根本不在乎。 - James King
@JamesKing 看了 DataTable 的源代码并更新了我的回答。这是故意的,但如果你问微软他们是否认为这是理想的,我非常怀疑。 - Eren Ersönmez

0

正如李在评论中指出的那样,DataTable是可处理的,因为它从MarshalByValueComponent继承了这个特性。Dispose()不会导致稍后抛出Disposed异常,这是一个偶然事件。嗯,这并不是真正的“偶然事件”,但没有任何东西可以防止框架的后续版本执行某些操作,这些操作确实会导致该异常。

我认为依赖于此是一个坏主意,我会将使用DataTable的代码移动到包装它的using内部。


-4
根据使用文档的说明:
通常情况下,当你使用一个IDisposable对象时,你应该在using语句中声明和实例化它。using语句以正确的方式调用对象的Dispose方法,并且(当你像之前展示的那样使用它)还会导致对象本身在Dispose被调用后立即超出作用域。在using块内,对象是只读的,不能被修改或重新分配。
  1. 是的。请参考下面的引用。
  2. N/A
  3. N/A。如果你查看IDisposable.Dispose(),它指出“执行与释放、释放或重置非托管资源相关的应用程序定义任务。”如果可以提供功能而不使用托管资源,则不需要释放对象以防止访问该功能。
  4. 实现了你正在访问的方法或属性的类的开发人员添加了代码来检测对象是否已释放,并根据需要抛出异常。

另外还有来自《使用》documentation的说明:

您可以实例化资源对象,然后将变量传递给using语句,但这不是最佳实践。在这种情况下,即使控件离开using块后,该对象仍然在作用域中,但可能不再访问其未受管控的资源。换句话说,它将不再被完全初始化。如果您尝试在using块之外使用该对象,则有可能引起异常。因此,通常最好在using语句中实例化对象并将其范围限制为using块。

简而言之,validationTable被处理且不再访问其未受管理的资源,但是托管资源(数据的本地副本)仍然可用。 这是假设ValidationDataTable已经正确实现的情况。 由于我在Google或MSDN上都没有找到它,所以我假设它是一个内部类,因此任何事情都是可能发生的。


1
"validationTable在using语句中未被声明和实例化,因此不会被处理" -- 这是不正确的。using语句允许在语句中声明变量,但并不要求。所引用的对象仍将被处理。 - Peter Duniho
@PeterDuniho,这个问题已经解决了。另外,请提供支持你说法的证据。如果我说“天空是紫色的”,而你说“不,天空是绿色的”,那么什么也不会改变。 - Trisped

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接