15

I happy coded quite a project that works fine and do not manifest any oddities at runtime. So I've decided to run static code analysis tool (I'm using Visual Studio 2010). It came out that rule CA2000 is being violated, message as follows:

Warning - CA2000 : Microsoft.Reliability : In method 'Bar.getDefaultFoo()', call System.IDisposable.Dispose on object 'new Foo()' before all references to it are out of scope.

The code refered goes like this:

private static IFoo getDefaultFoo()
{
    return (Baz.canIDoIt()) ? new Foo() : null;
}

I thought myself: maybe conditional expression spoils the logic (mine or validator's). Changed to this:

private static IFoo getDefaultFoo()
{
    IFoo ret = null;
    if (Baz.canIDoIt())
    {
        retFoo = new Foo();
    }
    return ret;
}

Same thing happened again, but now the object was referred to as retFoo. I've googled, I've msdn'ed, I've stackoverflow'ed. Found this article. There are no operations I need done after creating the object. I only need to return the reference to it. However, I have tried to apply the pattern suggested in OpenPort2 example. Now code looks like this:

private static IFoo getDefaultFoo()
{
    Foo tempFoo = null;
    Foo retFoo = null;
    try
    {
        if (Baz.canIDoIt())
        {
            tempFoo = new Foo();
        }
        retFoo= tempFoo;
        tempFoo = null;
    }
    finally
    {
        if (tempFoo != null)
        {
            tempFoo.Dispose();
        }
    }
    return retFoo;
}

Same message again, but tempFoo variable is rule violator this time. So basically, code went twisted, longer, little irrational, unnecesarrily complex and does the very same, but slower.

I've also found this question, where same rule seems to attack a valid code in similar manner. And there questioner is adviced to ignore the warning. I've also read this thread and a mass of similar questions.

Is there anything I missed? Is the rule bugged / irrelevant? What should I do? Ignore? Handle in some magickal way? Maybe apply some design pattern?

Edit:

After Nicole's request, I'm submiting the whole related code in form I also tried of using.

public class DisposableFooTest
{
    public interface IFoo
    {
        void bar();
    }

    public class Foo : IFoo, IDisposable
    {
        public void bar()
        {
            Console.Out.WriteLine("Foo baring now");
        }

        public void Dispose()
        {
            // actual Dispose implementation is irrelevant, or maybe it is?
            // anyway I followed microsoft dispose pattern
            // with Dispose(bool disposing)
        }
    }

    public static class Baz
    {
        private static bool toggle = false;
        public static bool canIDoIt()
        {
            toggle ^= true;
            return toggle;
        }
    }

    private static IFoo getDefaultFoo()
    {
        IFoo result = null;
        try
        {
            if (Baz.canIDoIt())
            {
                result = new Foo();
            }

            return result;
        }
        catch
        {
            if (result != null)
            {
                (result as IDisposable).Dispose();
                // IFoo does not inherit from IDisposable, hence the cast
            }

            throw;
        }
    }

    public static void Main()
    {
        IFoo bar = getDefaultFoo();
    }
}

Analysis report contain the folowing:
`CA2000 : Microsoft.Reliability : In method 'DisposableFooTest.getDefaultFoo()', call System.IDisposable.Dispose on object 'result' before all references to it are out of scope. %%projectpath%%\DisposableFooTest.cs 44 Test

Edit2:

The following approach resolved CA2000 problem:

private static IFoo getDefaultFoo()
{
    Foo result = null;
    try
    {
        if (Baz.canIDoIt())
        {
            result = new Foo();
        }
        return result;
    }
    finally
    {
        if (result != null)
        {
            result.Dispose();
        }
    }
}

Unfortunately, I can't go that way. What is more, I'd rather expect following object-oriented principles, good practices and guidelines to simplify the code, make it readable, maintanable and extendible. I doubt anyone read it as intended: give the Foo if possible, or null otherwise.

Community
  • 1
  • 1
Krzysztof Jabłoński
  • 1,890
  • 1
  • 20
  • 29
  • 1
    Side note: while warning itself is not applicable (see @ReedCopsey answer), make sure that whatever code uses real getDefaultFoo actually disposes the result (and real IFoo inherits from IDisposable) – Alexei Levenkov Jan 24 '13 at 00:52
  • I'm quite sure. The owner of the object is responsible for proper disposal of it if needed. Thanks for hint though. – Krzysztof Jabłoński Jan 24 '13 at 00:58

4 Answers4

10

This is a false positive warning. There is no way to return an appropriate instance of IFoo, if IFoo implements IDisposable, without the code analysis tool warning you that you're not disposing of it properly.

The code analysis doesn't analyze your intent or logic, it just tries to warn about common errors. In this case, it "looks like" you're using an IDisposable object and not calling Dispose(). Here, you're doing it by design, as you want your method to return a new instance and its acting as a form of factory method.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • I understand that I should revert the method to look like it was at first. Thanks for you answer. – Krzysztof Jabłoński Jan 24 '13 at 00:51
  • @KrzysztofJabłoński Yes - I personally find the first option the most understandable. You'll just have to ignore the warning. – Reed Copsey Jan 24 '13 at 00:53
  • 2
    @ReedCopsey: It's actually quite feasible to return a disposable from a factory method without triggering CA2000 as long as one disposes on all potential error paths. It's not necessarily _worthwhile_, but it is possible. – Nicole Calinoiu Jan 24 '13 at 13:43
  • 1
    @NicoleCalinoiu is absolutely right: the warning will only be issued if there's a path in which the return value is created but won't be returned. – Crono Feb 05 '14 at 18:21
8

The problem here isn't something that your C# code is explicitly doing, but rather that the generated IL is using multiple instructions to accomplish what looks like one "step" in the C# code.

For example, in the third version (with the try/finally), the return retFoo line actually involves assignment of an additional variable that is generated by the compiler and that you cannot see in your original code. Since this assignment takes place outside the try/finally block, it really does introduce potential for an unhandled exception to leave your disposable Foo instance "orphaned".

Here's a version that will avoid the potential problem (and pass CA2000 screening):

private static IFoo getDefaultFoo()
{
    IFoo result = null;
    try
    {
        if (Baz.canIDoIt())
        {
            result = new Foo();
        }

        return result;
    }
    catch
    {
        if (result != null)
        {
            result.Dispose();
        }

        throw;
    }
}

Obviously, that hampers readability and maintainability quite a bit as compared to the first version, so you'll have to decide whether the likelihood of orphaning and the consequences of not disposing an orphan actually merit the code change, or whether suppression of the CA2000 violation might be preferable in this particular case.

Nicole Calinoiu
  • 20,843
  • 2
  • 44
  • 49
  • Thank you for your answer. I've just checked this approach on actual code. It does not resolve the CA2000 validation problem. – Krzysztof Jabłoński Jan 24 '13 at 13:50
  • I've just extended my question with the whole test code without any changes. – Krzysztof Jabłoński Jan 24 '13 at 15:02
  • 2
    The problem in your latest implementation is the cast prior to disposition, which is preventing CA2000 from recognizing that the disposition addresses the potential problem. If you only ever return a `Foo` or `null`, you can fix this by changing the declared type of the `result` variable to `Foo`. Otherwise, you should cast before the `null` comparison in the exception handler, but you'll still end up needing to suppress the CA2000 violation as a false positive. – Nicole Calinoiu Jan 24 '13 at 19:36
  • You may be right. After changing method return type and `result` local variable type to concrete `Foo`, the violation vanished. Unfortunately I can't go that way. – Krzysztof Jabłoński Jan 24 '13 at 19:45
  • 1
    You don't need to change the method return type, just the type of the local `result` variable, in order to avoid the cast and allow CA2000 to recognize the disposition. – Nicole Calinoiu Jan 24 '13 at 21:35
  • Very well observed, @NicoleCalinoiu! This should be marked as the answer. +1 – Crono Feb 05 '14 at 18:06
2

Static analysis is basically complaining for the following reason:

  1. The IFoo interface does not inherit from IDisposable,
  2. But you are returning a concrete implementation which must be disposed, and callers won't have a way of knowing this.

In other words, the caller of your GetDefaultFoo method expects an IFoo implementation, but doesn't have a clue that your implementation needs to be explicitly disposed, and will therefore probably not dispose it. If this was a common practice in other libraries, you would have to manually check if an item implements IDisposable for, well, every possible implementation of any possible interface.

Obviously, there is something wrong with that.

IMHO, the cleanest way of fixing this would be to make IFoo inherit from IDisposable:

public interface IFoo : IDisposable
{
    void Bar();
}

This way, callers know that they any possible implementation they receive must be disposed. And this will also allow the static analyzer to check all places where you are using an IFoo object, and warn you if you are not disposing them properly.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • Thank you for your opinion. That's right, analyzer wouldn't complain if IFoo interited from IDisposable. The problem is, that some implementations if IFoo might be IDisposable, but not necesarily all. And my IFoo interface has to ensure a class can actually Bar, no matter if it disposes or not. I'd have to implement Dispose method in classes that don't need it and leave it empty. – Krzysztof Jabłoński Jan 25 '13 at 21:12
  • Found [this answer](http://stackoverflow.com/a/6997732/1598508) on that particular concern. Responder claims it could be bad design, but commentors immediately disagreed. Seems to be a matter of taste. Moreover, disposable foos can't be used within `using` clause, because they are long-term object and are used in many iterations of framework induced calls. – Krzysztof Jabłoński Jan 25 '13 at 22:50
  • Yes, I agree with @supercat's comment on [that answer](http://stackoverflow.com/a/6997732/69809), *Interfaces should inherit IDisposable (...) if the last entity using it may not know its specific type*. I usually follow this rule. But as the post said, it depends on the concrete problem. For example, your factory method only has a single `IFoo` implementation right now (but that's probably just an example). And why does the concrete `Foo` class implement `IDisposable` at all? Does it wrap a different `IDisposable`? Does this object need to be disposed "as soon as possible"? – vgru Jan 27 '13 at 20:45
  • Interesting point of view. Thank you for your support. Your assumptions are correct - I have several implementations of IFoo, of which most wrap disposable objects. Some don't, though, and I could imagine many more implementations with nothing to dispose at all. All those IFoos will live quiet long (minutes), but are sometimes replaced by anothers and then resources should be disposed. I guess your right, that caller of factory method may be unaware of IDisposable nature of returned object. So maybe IFoo should inherit IDisposable explicitly in this particular case. Thanks. – Krzysztof Jabłoński Jan 29 '13 at 14:51
-1

When returning an IDisposable object, in the event of a null value, dispose of the object and return null in the method - this should clear up the 'warning/recommendation' message associated with your 'private static IFoo getDefaultFoo()' method. Off course you still need to deal with the object as an IDisposable (using or dispose) in your calling routine.

private static IFoo getDefaultFoo()
{
    IFoo ret = null;
    if (Baz.canIDoIt())
    {
        retFoo = new Foo();
    }
    return ret;
}

Change to

private static IFoo getDefaultFoo()
{
    IFoo ret = null;
    if (Baz.canIDoIt())
    {
        retFoo = new Foo();
    }
    if (ret == null)
    {
        ret.dispose();
        return null;
    }
    else
        return ret;
}
Georg
  • 404
  • 5
  • 7
  • I appreciate your efforts, yet after almost 7 years I care a bit less about the warning, because the project is now closed. – Krzysztof Jabłoński Nov 29 '19 at 22:35
  • Yet still, I analyzed your answer, for which I am thankful, and I have two remarks. Firstly, I have an impression that you missed the point of the method `getDefaultFoo()`. It's very purpose is to provide a meaningful `IFoo` instance, in particular not broken or disposed. Secondly, calling `ret.dispose()` when `ret` is `null` will inevitably lead to `NullReferenceException` being thrown. – Krzysztof Jabłoński Nov 29 '19 at 22:39