问题
As describe this article, about usage of using on IDisposable objects, it says one interesting words:
...using block, the Dispose method is automatically called sometime after the block ends. (It may not be immediate; it depends on the CLR.)
Interesting here is "It may not be immediate; it depends on the CLR". Can anyone provide more details on this? Because we have some strange situations where it seems that on code using(new MyDisposable()) {...}, after end of block } it does NOT immediately calls Dispose method on MyDisposable instance, but some time later.
UPDATE: Conclusion for me, it seems to me that i have problem elsewhere. I think that Dispose method can be called some time later after using block ends. But when it is not like that, than i must find problem somewhere else in my code. Thanks for responses!
回答1:
I'm a little skeptical of that statement, and think they meant something else (perhaps garbage collection). A using statement is just syntactic sugar for a try/finally block where the finally block calls dispose. Given this C#:
using (var fs = new FileStream("C:\\blah.txt", FileMode.CreateNew))
{
fs.WriteByte(7);
}
The IL looks like this:
//snipped
L_000e: nop
L_000f: ldstr "C:\\blah.txt"
L_0014: ldc.i4.1
L_0015: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode)
L_001a: stloc.0
L_001b: nop
L_001c: ldloc.0
L_001d: ldc.i4.7
L_001e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8)
L_0023: nop
L_0024: nop
L_0025: leave.s L_0037
L_0027: ldloc.0
L_0028: ldnull
L_0029: ceq
L_002b: stloc.1
L_002c: ldloc.1
L_002d: brtrue.s L_0036
L_002f: ldloc.0
L_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0035: nop
L_0036: endfinally
L_0037: nop
L_0038: nop
L_0039: ret
.try L_001b to L_0027 finally handler L_0027 to L_0037
Notice on the last line it's just a .try and .finally. This is also indicated in the C# spec: http://msdn.microsoft.com/en-us/library/aa664736.aspx
回答2:
using (SomeDisposableResource resource = new SomeDisposableResource())
{
// TODO: use the resource
}
is equivalent to:
SomeDisposableResource resource = new SomeDisposableResource();
try
{
// TODO: use the resource
}
finally
{
if (resource != null)
{
((IDisposable)resource).Dispose();
}
}
so, up to you to draw conclusions. Everything depends on how you define immediate. In a multithreaded environment other actions could be performed between the try
block and the disposal of the resource but as it is wrapped in a finally
block it is guaranteed that the Dispose method will be called.
回答3:
Strange... very strange...
The article is probably wrong. The using statement is compiled exactly as
MyDisposableObject obj = new MyDisposableObject()
try
{
obj.Use();
}
finally
{
if (obj!=null) obj.Dispose();
}
The Dispose method is explicitly called in the finally
block, unlike the destructor/Finalize
method which is called prior to collection but at GC's discretion.
I think it's an error in the article. At most... that sometime may refer to thread scheduling. If CLR decides to schedule other threads once hit the finally, then you might wait up some very little time on 100% loaded CPU and higher-priority tasks running to run Dispose.
It's important for Dispose to be synchronous!!! Think about this example about managed resources
public void Log(string message)
{
using(StreamWriter sw = new StreamWriter(File.Append(path)))
{
sw.WriteLine(message);
}
}
public static void Main()
{
Log("Hello");
Log("World");
}
The Dispose
call, on streams and files, actually closes them. If what's written in the article was ever true, you would be calling the second Log
with an open file, thus causing IOException
immediately!
回答4:
I don't know where that comes from, and that contradicts everything else I have seen about the using
statement, for example this article that says that a using
block like this:
using (Font font1 = new Font("Arial", 10.0f)) {
byte charset = font1.GdiCharSet;
}
is implemented like this:
Font font1 = new Font("Arial", 10.0f);
try {
byte charset = font1.GdiCharSet;
} finally {
if (font1 != null) {
((IDisposable)font1).Dispose();
}
}
So, the Dispose
method will be called before the next statement following the using
block.
The person writing the article might have it confused with how an object will be garbage collected some time after it's not used any more, or how disposable objects that are not disposed have their finalizer called by a background thread.
来源:https://stackoverflow.com/questions/4060684/using-statement-on-idisposable-object-delay-of-calling-dispose-method