引用using()块

时间:2016-04-20 18:17:47

标签: c# dispose using idisposable using-statement

关于这个问题有很多关于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但已经处理掉了但没有进行垃圾回收 - 但仍然应该在ObjectDisposedException调用上抛出.ToDictionary()。但是这段代码很乐意构建一个有效的字典并继续前进。

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

我的问题:

  1. 此代码是否有效并且表现得如此?
  2. 如果没有,我们只看到一个废话是否成功?
  3. 是否有关于DataTable的特定内容允许在放置对象后进行访问 - 类似于GZipStream类要求您处理对象以刷新流的方式,因此允许调用在对象被处置后到.ToArray().GetBuffer()
  4. ...当你调用方法时,实际上是什么导致抛出ObjectDisposedException?我总是认为它来自.NET框架本身。
  5. 澄清:

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

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

    此外,如果您投票或投票关闭它,请发表评论原因。

3 个答案:

答案 0 :(得分:5)

我认为你缺少的部分是“处理”一个对象除了程序员在IDisposable.Dispose实现中定义的内容之外没有做任何事情。除了为using语句提供支持之外,语言或框架不会做任何特殊操作。

使用using语句,语言只提供以下内容:如果您的对象实现了名为IDisposable的特定接口,那么它承诺在Dispose方法存在时调用using方法{ {1}}阻止。而已。它不知道哪些物体已被“处置”或没有。它不会以特殊方式跟踪已处置的对象而抛出ObjectDisposedException

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

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

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

更新:要回答评论,看起来DataTable不会直接实现IDisposable,但它是基类(MarshalByValueComponent)。他们必须继承该基类才能支持WinForms设计师的体验。在设计模式之外,Dispose不会改变任何东西。因此,您可以安全地忽略它以供正常使用。换句话说,您不需要在using块中使用它。

这是正常的吗?不可以。通常,IDisposable个对象意味着要放置在正常生命周期的某个地方。如果IDisposable不需要处理,那肯定会让人感到困惑。

答案 1 :(得分:0)

Lee在评论中指出,DataTable是一次性的,因为它继承了MarshalByValueComponent。 Dispose()不会做任何会导致稍后抛出Disposed异常的事故。好吧,它本身并不是一个意外,但没有什么可以阻止更高版本的框架执行引起异常的事情。

我认为依靠这个是一个坏主意,我会移动使用DataTable的代码来包装它。

答案 2 :(得分:-4)

根据EPEL

  

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

  1. 是。见下面的引用。
  2. N / A
  3. N / A。如果查看documentation on using,它会声明“执行与释放,释放或重置非托管资源相关的应用程序定义的任务”。如果可以在没有托管资源的情况下提供功能,则不需要配置对象来阻止访问该功能。
  4. 实现您正在访问的方法或属性的类的开发人员添加代码以检测对象是否已被处置并根据需要抛出异常。
  5. 同样来自IDisposable.Dispose()

      

    您可以实例化资源对象,然后将变量传递给using语句,但这不是最佳做法。在这种情况下,在控制离开使用块之后,对象仍然在范围内,即使它可能不再能够访问其非托管资源。换句话说,它将不再完全初始化。如果您尝试使用using块之外的对象,则可能会导致抛出异常。因此,通常最好在using语句中实例化对象,并将其范围限制为使用块。

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