在使用()块之外引用一个对象
在这个主题上有很多关于这个问题的问题,但我还没有找到一个涵盖我特别需要了解的问题。
我的一位开发人员编写了这段代码:
//
// 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...
//
我的理解:验证表已被处置,但在最后一行代码中被引用时不收集垃圾 - 但仍应在.ToDictionary()
调用中抛出ObjectDisposedException
。 但是,这段代码愉快地构建了一个有效的字典并继续前进。
我有理论,但找不到任何明确的证实或击倒他们。 代码可以重写十几种方法来避免这个问题; 这不是问题。 我只需要知道我理解的差距是什么。
我的问题:
DataTable
允许在对象处理后进行访问 - 类似于GZipStream
类要求您处理对象以刷新流的方式,因此允许在调用后对.ToArray()
和.GetBuffer()
进行调用该物体已被丢弃? 。
澄清:
这是一个.NET框架问题。 共识是我的理解是正确的 - DataTable本身必须抛出ObjectDisposedException
。 除了它没有。 DataTable源代码中没有任何地方 - 因此我问。 我认为框架可以确保ObjectDisposedException处理后,这显然不是这种情况...不像GZipStream,它只允许访问Dispose()后面的两个方法DataTable DGAF。 精细。
因此,让我重述一下这个问题:DataTable内部有什么会炸毁我们,因为允许调用一个处理过的表? 我可以假设,微软并没有清理任何内部的东西,只要对象在范围内,所有的属性和值都会在那里,不会受到影响 - 这似乎并不是一个安全的假设。 这段代码无论如何 - 我只是想了解是否有一个故意的原因微软允许在Dispose()
之后访问DataTable,或者这是一个疏忽,不关心等等。
另外,如果您不满意或者投票结束,请留下评论原因。
我认为你丢失的部分是这样一个事实,即“处理”一个对象除了程序员在IDisposable.Dispose
实现中定义的内容之外不会执行其他任何操作。 语言或框架除了为using
语句提供支持之外不会做任何特殊的事情。
使用using
语句时,语言只提供以下内容:如果您的对象实现了此特定接口(称为IDisposable
,那么它承诺在存在using
块时调用Dispose
方法。 而已。 它不知道哪些物体被“处置”或没有。 它不会通过以特殊方式跟踪处置对象来抛出ObjectDisposedException
。
什么抛出一个ObjectDisposedException
? 那么,实现这个IDisposable
类型的程序员需要在这里编写这样的代码:
void DoMoreWork()
{
if(_iHaveBeenDisposedAlready)
throw new ObjectDisposedException(null);
...
所以在你的情况下,如果ValidationDataTable
实现方式不能跟踪它是否被处置,并且它将数据存储在内存中,那么它将像往常一样工作。 语言或框架不会阻止这种情况的发生。
更新:回答评论,它看起来像DataTable
不直接实现IDisposable
但它是基类( MarshalByValueComponent
)。 他们必须从该基类继承以支持WinForms设计器体验。 在设计模式之外, Dispose
不会改变任何东西。 所以你可以放心地忽略它的正常使用。 换句话说,你不需要在using
块中使用它。
这是正常的吗? 不,通常, IDisposable
对象意味着在正常生命周期的某个地方处置。 拥有无需处置的IDisposable
肯定会让人感到困惑。
正如Lee在他的评论中指出的那样,DataTable是一次性的,因为它继承了MarshalByValueComponent。 Dispose()不会做任何会导致Disposed异常被抛出的事情。 好吧,这本身并不是一个偶然的事情,但是没有什么能够阻止该框架的后期版本去做一些导致该异常的事情。
我认为这是一个不错的主意,我会将使用DataTable的代码放在使用它的代码中进行包装。
根据using
文件:
通常,当您使用IDisposable对象时,您应该在using语句中声明并实例化它。 using语句以正确的方式调用对象上的Dispose方法,并且(如前所示使用它时)也会在调用Dispose后立即使对象本身超出范围。 在使用块中,对象是只读的,不能被修改或重新分配。
此外,从上的文档using
:
您可以实例化资源对象,然后将该变量传递给using语句,但这不是最佳实践。 在这种情况下,尽管可能不再有权访问其非托管资源,但在控制离开使用块后,对象仍保留在范围内。 换句话说,它将不再完全初始化。 如果您尝试在使用块之外使用该对象,则可能会导致引发异常。 出于这个原因,通常最好在using语句中实例化对象,并将其范围限制为using块。
简而言之, validationTable
被丢弃,不再有权访问其非托管资源,但托管资源(数据的本地副本)仍然可用。 这是假设ValidationDataTable
已正确实施。 由于我没有找到它,虽然谷歌或MSDN,我认为这是一个室内类,所以什么都可以。