Search code examples
c#disposeusingidisposableusing-statement

Referencing an object outside of using() block


There are many questions on this subject on SO, but I haven't found one that covers what I specifically need to understand.

One of my developers wrote this code:

//

    //  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...

//

My understanding: The validationTable has been disposed but not garbage collected when it's referenced in the last code line – but should still have thrown an ObjectDisposedException on the .ToDictionary() call. But this code happily builds a valid dictionary and keeps going.

I have theories, but can't find anything definitive to confirm or shoot down any of them. And the code can be rewritten a dozen ways to avoid the issue; that's not a problem. I just need to know what the gap in my understanding is.

My questions:

  1. Is this code valid and behaving as it should?
  2. If not, is the success we're seeing just a crapshoot?
  3. Is there something specific about a DataTable that allows access after the object is disposed - similar to the way a GZipStream class requires you to dispose of the object to flush the stream, and therefore allows calls to .ToArray() and .GetBuffer() after the object has been disposed?
  4. ... What actually causes an ObjectDisposedException to be thrown when you call methods? I'd always assumed that was coming from the .NET framework itself.

.

CLARIFICATION:

This is a .NET Framework question. The consensus is that my understanding is correct - the DataTable itself would have to throw the ObjectDisposedException. Except that it doesn't. Not anywhere in the DataTable source code - hence my asking. I assumed that the framework would ensure an ObjectDisposedException after it had been disposed, which apparently isn't the case... unlike the GZipStream, which only allows access to two methods after Dispose(), the DataTable DGAF. Fine.

So let me rephrase the question: Is there anything internal to the DataTable that will bomb us because calls to a disposed table are allowed? I can assume that Microsoft hasn't cleaned up anything internally, that all properties and values will be there, untouched, for as long as the object is in scope - that just doesn't seem like a safe assumption. This code is going away regardless - I just wanted to understand if there was a deliberate reason Microsoft allows access to the DataTable after Dispose() or it was an oversight, not caring, etc.

Also, if you downvote a question or vote to close it, please leave a comment why.


Solution

  • I think the part you're missing is the fact that "disposing" an object doesn't do anything other than what the programmer defined in the IDisposable.Dispose implementation. The language or the framework doesn't do anything special other than providing support for the using statement.

    With the using statement, the language just provides the following: if your object implements this specific interface called IDisposable, then it promises to call the Dispose method when it exists the using block. That's it. It has no knowledge of which objects have been "disposed" or not. It doesn't throw ObjectDisposedException by keeping track of disposed objects in a special way.

    What throws an ObjectDisposedException? Well, the programmer who implemented that IDisposable type would need to write a code like this somewhere in there:

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

    So in your case, if the ValidationDataTable was implemented in a way that doesn't keep track of whether it was disposed or not, and it stores it's data in memory, then it will just work as usual. The language or the framework doesn't stop that from happening.


    UPDATE: to answer the comments, it looks like DataTable doesn't directly implement IDisposable but it's base class (MarshalByValueComponent) does. They had to inherit from that base class in order to support the WinForms designer experience. Outside the design mode, Dispose doesn't mutate anything. So you can safely ignore it for your normal usage. In other words, you don't need to use it within a using block.

    Is this normal? No. Usually, IDisposable objects are meant to be disposed somewhere down their normal life-cycle. It surely is confusing to have an IDisposable that doesn't require disposing.