So, after discovering t开发者_C百科hat the Bitmap class expects the original stream to stay open for the life of the image or bitmap, I decided to find out if the Bitmap class actually closes the stream when it is disposed.
Looking at the source code, the Bitmap and Image classes create a GPStream instance to wrap the stream, but do not store a reference to either the GPStream or the Stream instance.
num = SafeNativeMethods.Gdip.GdipLoadImageFromStreamICM(new GPStream(stream), out zero);
Now, the GPStream class (internal), does not implement a Release or Dispose method - nothing that would allow GDI to close or dispose of the stream. And since the Image/Bitmap class doesn't keep a reference to the GPStream instance, it seems that there is absolutely no way for either GDI, Drawing.Bitmap, or Drawing.Stream to close the stream properly.
I could subclass Bitmap to fix this, but, oh wait, it's sealed.
Please tell me I'm wrong, and that MS didn't just make it impossible to write code that doesn't leak resources with their API.
Keep in mind (a), Bitmap has no managed reference to the stream, meaning GC will collect it while it is still in use, and (b) .NET APIs take Bitmap/Image references and aren't deterministic about when they're done with them.
Since you supply the stream in this example, I'd imagine you are responsible for disposing it.
It is a good practice to have the method that opens a stream, close it as well. That way it is easier to keep track of leaks. It would be quite strange to have an other object closing the stream that you opened.
Because bitmap can't guarantee in which order the destructor is called it will not close the stream because it may already have been closed with its own destructor during garbage collection. Jeffrey Richter's CLR via C# has a chapter on memory management that explains with much more clarity than I can.
An easy workaround to the problem is:
var image = new Bitmap(stream);
image.Tag = stream;
Now the stream is referenced by the image and won't be garbage collected before the image is. If your stream happens to be a MemoryStream, it doesn't need to be disposed (its Dispose is a no-op). If not, you can dispose it when you dispose the image, or just let the GC do it when it gets around to it.
精彩评论