CA2000 Warning call Dispose on object

Z

za

Hello,

I get a CA2000 warning on the following code.

public class Taskbar
{
private static Taskbar _default;

private bool _isSynchronized;

public static Taskbar Default
{
if (_default == null)
{
_default = Taskbar.Synchronized(new Taskbar());
}

return _default;
}

public static Taskbar Synchronized(Taskbar taskbar)
{
taskbar._isSynchronized = true;
return taskbar;
}
}

Can somebody explain me how to resolve the CA2000 warnings on the code?

Regards,

Fred
 
Z

za

Peter Duniho said:
There's not enough context to know for sure what an appropriate resolution
would be. Especially since the code you did post doesn't compile, never
mind include any types that implement IDisposable.

But in general, you'll get that warning when it appears that there's a way
for an object implementing IDisposable to not be visible after a method
returns, but on which the Dispose() method has not been called before the
method returns.

Post a concise-but-complete code example that reliably reproduces the
warning if you want more specific advice.

Finally, note that it is a good idea to make static members of a type
thread-safe, even if instances of the class are not intended to be
thread-safe. This is especially true for singletons and singleton-like
members, such as your "Default" member.

Pete

Hi Pete,

using System;
using System.Configuration;

namespace CA2000
{
internal class Settings1 : ApplicationSettingsBase, IDisposable
{
private static Settings1 defaultInstance =
((Settings1)(ApplicationSettingsBase.Synchronized(new Settings1())));

public static Settings1 Default
{
get
{
return defaultInstance;
}
}

public void Dispose()
{

}
}
}

Maybe this is the simplest sample to reproduce the CA2000 error.

Regards,

Fred
 
Z

za

Peter Duniho said:
Well, actually.the above example doesn't need the Default property in
order to reproduce the warning. That said, it's a good example, and
provides plenty to work with in terms of answering the question.

Unfortunately, I'm not there's a good _answer_. :)

As you're probably already aware, the bottom line issue here is that an
object is allocated, and then as far as the code analysis can tell, is not
disposed of before you no longer have a reference to it.

Of course, the object returned by the Synchronized() method in fact refers
to the original object (indeed, _is_ the original object), and so the
reference still exists, and so doesn't need to be disposed (and in fact,
cannot be). But there's no direct way for the code analyzer to know that.
It can't make assumptions about what the Synchronized() method does, and
it doesn't do any sort of called-method inspection to make its
determinations.

As far as potential resolutions go, here is a version of the code that
doesn't cause the warning:

using System;
using System.Configuration;

namespace CA2000
{
internal class Settings1 : ApplicationSettingsBase, IDisposable
{
private static Settings1 defaultInstance;

static Settings1()
{
defaultInstance = new Settings1();

defaultInstance =
(Settings1)ApplicationSettingsBase.Synchronized(defaultInstance);
}

public void Dispose()
{

}
}
}

Note that by writing the static constructor explicitly, _and_ by
assigning the original Settings1() object to your field before passing
it to the Synchronized() method, that's enough to convince code analysis
that you've let the object reference escape the constructor, and thus it
does not need to be disposed of before the constructor returns.

I find this resolution unfortunate in two ways:

. First, it would be nice if there were some way to explain to the
analyzer that in this case, passing the object reference to the
Synchronized() method does in fact preserve a reference to the object
after the constructor returns.

. Second, it concerns me that the code analysis doesn't pick up on the
fact that the "defaultInstance" field is being overwritten, which absent
any specific knowledge of the Synchronized() method (which clearly the
analyzer does not have) _should_ in fact mean that the original object
reference has been lost before the object has been disposed.

I find this second issue especially ironic, because it appears that when
the variable is a local variable (i.e. if you try to move the
initialization into a helper method with a try/catch to deal with
disposing the original object on error), the analyzer _does_ in fact pick
up on the fact that it's been overwritten, and so returning the new value
does not satisfy the analyzer with respect to the original value.

I don't really see a good way around the first issue, other than just
suppressing the warning (which is a blunt way to give the analyzer that
information). The second issue concerns me because it's possible someone
at Microsoft will eventually decide it's a bug that the analyzer doesn't
figure that out and will fix the bug, and then my proposed work-around
isn't going to help.

I think given all the above, personally I'd just suppress the warning and
be done with it. The fact is, it's a situation that given the kind of
analysis used for that rule, I think really cannot be detected as safe by
the analyzer, so you're left helping it out by suppressing the warning
explicitly.

Pete

Hi Pete,

Thank you for your detailled explanation and also for the third work around!
Personally I think that it is a bug in the analyzer, but i am not sure about
it :)

I've also tried this code, but it gives a CA2000 too.

using System;
using System.Configuration;

namespace CA2000
{
internal class Settings1 : ApplicationSettingsBase, IDisposable
{
private static Settings1 defaultInstance; // =
((Settings1)(ApplicationSettingsBase.Synchronized(new Settings1())));

public static Settings1 Default
{
get
{
if (defaultInstance == null)
{
Settings1 temp = null;
try
{
temp = new Settings1();
temp = (Settings1)Settings1.Synchronized(temp);
defaultInstance = temp;
temp = null;
}
finally
{
if (temp != null)
{
temp.Dispose();
}
}
}

return defaultInstance;
}
}

public void Dispose()
{

}
}
}

Regards,

Fred
 
Z

za

Peter Duniho said:
za said:
[...]
Thank you for your detailled explanation and also for the third work
around! Personally I think that it is a bug in the analyzer, but i am not
sure about it :)

I would not call it a bug. There really is no practical way for the
analyzer to know the difference between a method that returns an unrelated
object when you pass in an object, and a method that returns a related (or
even identical) object to the one passed in.
I've also tried this code, but it gives a CA2000 too.

[...]
try
{
temp = new Settings1();
temp = (Settings1)Settings1.Synchronized(temp);
defaultInstance = temp;
temp = null;
} [...]

Right. But because the analyzer _can_ tell that the local variable "temp"
is set to null, thus losing the reference, before being disposed the
warning is still emitted.

The basic logic here is similar to that used for "definite assignment".
That is, the analyzer is looking for "definite 'return'", where 'return'
simply means that the object reference gets _out_ of the method. Calling
a method can't satisfy that requirement, because while the method _could_
retain the reference somehow, it doesn't _have_ to.

Reasoning that a false positive is better than a false negative, the
analyzer always warns unless it can be _sure_ that the reference has left
the method.

In the end, any work-around for the issue is likely to result in code that
is much more awkward than would normally be needed. Unlike the definite
assignment scenario, where a single assignment is generally sufficient,
the work-arounds for CA2000 false-positives all involve tailoring the code
to suit the analyzer's particular mechanisms.

That's why, in spite of the presence of work-arounds, it would be my
inclination to just write the code the way you feel is most appropriate,
and then put a suppression in.

Pete

Hi Pete,

Thank you for your time and answers. I will supress the warning(s)

Regards,
Fred
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top