Jaska
Jaska

Reputation: 1430

Microsoft.usage warning when there's two nested using clauses

I'm wondering why does Visual Studio code analysis give a warning with this little bit of code:

byte[] data = File.ReadAllBytes("myImage.png");
using (MemoryStream mem = new MemoryStream(data))
using (Bitmap bmp = new Bitmap(mem))            
{
     // do something with the bitmap
}

The error is:

Object 'mem' can be disposed more than once in method ... To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.

And how to correct this? (Yes, I could load bitmap directly from file, but in my real project I have my own file format where multiple images are saved into one file and therefore I need a MemoryStream to load the data from a specific range in the file)

Upvotes: 0

Views: 303

Answers (4)

Reacher Gilt
Reacher Gilt

Reputation: 1813

with regards to the second reliability warning, it seems like when this situation occurs, code analysis will complain with either CA2000 or CA2202 regardless of what you do. There's multiple complaints about this, here's one for your enjoyment.

Just to see if it's particular to using syntax, I expanded out the using into a try catch block and you get the same behavior.

class Program
    {
        static void Main(string[] args)
        {
            byte[] data = {};
            //using (Bitmap bitmap = new Bitmap(new MemoryStream(data)))
            //{ }

            MemoryStream mem = null;
            Bitmap bitmap = null;
            try
            {
                mem = new MemoryStream(data);
                bitmap = new Bitmap(mem);

            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                if (null!= bitmap ) bitmap.Dispose();                
                // commenting this out will provoke a CA2000.
                // uncommenting this will provoke CA2202.
                // so pick your poison.
                // if (null != mem) mem.Dispose();
            }
        } 
    }

edit: There is, as you (Jaska) pointed out, a note from Microsoft about flagging intent about the memorystream in the Bitmap's using block:

class Program
    {
        static void Main(string[] args)
        {
            byte[] data = new byte[1000];
            MemoryStream mem = null;
            try
            {
                mem = new MemoryStream(data);
                using (Bitmap bmp = new Bitmap(mem))
                {
                    mem = null; // <-- this line will make both warning go away
                }
            }
            finally
            {
                if (mem != null)
                {
                    mem.Dispose();
                }
            } 
        } 
    }

I'm glad to see that this works, but at the same time it's a bit uglier than using an anonymous memorystream in the bitmap's constructor. Ah well.

Upvotes: 1

Jaska
Jaska

Reputation: 1430

Reacher-Gilt's link to ms-page led me to the solution, which will make both warnings disappear:

        byte[] data = new byte[1000];
        MemoryStream mem = null;
        try
        {
            mem = new MemoryStream(data);
            using (Bitmap bmp = new Bitmap(mem))
            {
                mem = null; // <-- this line will make both warning go away
            }
        }
        finally
        {
            if (mem != null)
            {
                mem.Dispose();
            }
        } 

Upvotes: 1

Reacher Gilt
Reacher Gilt

Reputation: 1813

Once you enter the second using statement, your Bitmap will "consume" the memorystream -- it becomes responsible for the memorystream, including its disposal. This is sensible, if you think about your usage: would you expect something else to be accessing mem after (or during) you've used it in your Bitmap's constructor? I'd say probably not.

The answer is to consolidate the two using statements together: using (Bitmap bitmap = new Bitmap(new MemoryStream(data))). That should take care of the CA warning.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500903

The problem is that Bitmap takes ownership of the stream it's given in its constructor. It will dispose of the stream - you don't need to. So all you need is:

using (Bitmap bmp = new Bitmap(new MemoryStream(data)))            
{
     // do something with the bitmap
}

Upvotes: 6

Related Questions