From: za on
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


From: Peter Duniho on
za wrote:
> 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?

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
From: za on

"Peter Duniho" <NpOeStPeAdM(a)NnOwSlPiAnMk.com> wrote in message
news:dcydndXHMpI2lZHRnZ2dnUVZ_hudnZ2d(a)posted.palinacquisition...
> za wrote:
>> 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?
>
> 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


From: Peter Duniho on
za wrote:
> 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.

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
From: Peter Duniho on
Peter Duniho wrote:
> [...]
> As far as potential resolutions go, here is a version of the code that
> doesn't cause the warning: [...]

Here's a different approach that also avoids the warning:

using System;
using System.Configuration;

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

public static Settings1 Default
{
get
{
if (!defaultInstance.IsSynchronized)
{
defaultInstance =
(Settings1)SettingsBase.Synchronized(defaultInstance);
}

return defaultInstance;
}
}

public void Dispose()
{

}
}
}

It requires the use of the getter, but given your previous example, I'm
assuming that's not actually a problem.

Unfortunately, it's somewhat "hackish" also. In particular, it doesn't
require synchronization in the getter only because this solution assumes
knowledge of the implementation of the Synchronized() method (i.e. the
method returns the original object, just having set the IsSynchronized
property to "true").

Of course, your original implementation appears to make that assumption
anyway (casting the returned object to Settings1), so maybe that's a
valid assumption to make in this context.

I still think I'd prefer an explicit suppression over twisting the code
like this just to satisfy the code analysis. :)

Pete