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:
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? .
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.
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.
As Lee points out in his comment, DataTable is disposable because it inherits that from MarshalByValueComponent. It is an accident that Dispose() doesn't do anything that would cause a Disposed exception to be thrown later. Well, it's not really an accident per se, but there is nothing to prevent a later version of the framework to do something that did cause that exception.
I think it is a bad idea to count on this, and I would move the code that uses the DataTable inside the using that wraps it.
According to the 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.
Also from the 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.
In short, validationTable
is disposed and no longer has access to its unmanaged resources, but the managed resources (the local copy of the data) are still available. That is assuming that ValidationDataTable
was implemented correctly. Since I am not finding it though google or msdn, I am assuming that it is an in house class, so anything goes.
上一篇: 我应该何时实施IDisposable?
下一篇: 在使用()块之外引用一个对象