27

I am doing a code review, and have found alot of code with the following format:

public MyResponse MyMethod(string arg)
{
    using (Tracer myTracer = new Tracer(Constants.TraceLog))
    {
        MyResponse abc = new MyResponse();

        // Some code

        return abc;
    }
}

When I run a code analysis I get a CA2000 warning Microsoft.Reliability

Should the code be rewritten as:

public MyResponse MyMethod(string arg)
{
   MyResponse abc = new MyResponse();

   using (Tracer myTracer = new Tracer(Constants.TraceLog))
   {
       // Some code
   }
   return abc;
}

Or does it not matter?

Edit

The line on which it is reporting the warning is:

MyResponse abc = new MyResponse();

MyResponse is a standard Dataset.

The full error message is:

Warning 150 CA2000 : Microsoft.Reliability : In method 'xxxxx(Guid, Guid)', object 'MyResponse ' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'MyResponse ' before all references to it are out of scope.

Machavity
  • 30,841
  • 27
  • 92
  • 100
Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252

5 Answers5

15

No, it doesn't matter.

The finally block that's implicitly generated by the using statement to handle disposal will execute no matter where you put the return.

Are you sure that the CA2000 relates to myTracer and not abc? I would guess that the warning is occurring because MyResponse implements IDisposable and you're not disposing abc before returning. (Either way, your suggested rewrite should make no difference to the warning.)

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • 1
    @smartcaveman - The question ends with the question "Or does it not matter?" so the statement at the beginning of this answer is perfectly fine. – Richard Szalay Apr 06 '11 at 14:36
  • @Richard, I didn't say it was wrong. I said it was confusing in context of the title. – smartcaveman Apr 06 '11 at 14:37
  • I don't want to be picky, but I don't understand why this answer has been upvoted that much. The first part has nothing to do with the provided warning and the second part doesn't provide a solution... – Daniel Hilgarth Apr 06 '11 at 15:24
  • 3
    @Daniel: The OP asks "is it ok to return from inside a using block" and "should the code be rewritten to move the return outside the using block". I'm answering those questions, as asked, and then *speculating* on the real underlying cause of the problem (speculating because the OP doesn't provide enough concrete details). Your own answer provides a solution to the (putative) real problem, I'm happy to reproduce something similar here if you think it's worthwhile. – LukeH Apr 06 '11 at 15:39
11

Your rewrite will not fix that CA2000 warning, because the problem is not the Tracer object, but the MyResponse object.
The documentation states:

The following are some situations where the using statement is not enough to protect IDisposable objects and can cause CA2000 to occur.
Returning a disposable object requires that the object is constructed in a try/finally block outside a using block.

To fix the warning without messing with the stack trace of your exceptions (<- click, it's a link), use this code:

public MyResponse MyMethod(string arg)
{
   MyResponse tmpResponse = null;
   MyResponse response = null;
   try
   {
       tmpResponse = new MyResponse();

       using (Tracer myTracer = new Tracer(Constants.TraceLog))
       {
           // Some code
       }

       response = tmpResponse;
       tmpResponse = null;
    }
    finally
    {
        if(tmpResponse != null)
            tmpResponse .Dispose();
    }
    return response;
}

Why? Please see the example in the linked documentation.

Community
  • 1
  • 1
Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • 1
    I think `try/catch` might be preferable, as it doesn't require you create a temp variable -- the cure here might be worse than the disease. – Jon Apr 06 '11 at 14:49
  • It completely depends on your situation, if you want to implement *something* that will fix this warning or not. I wouldn't because I don't see it as a problem, if that method isn't called very often. However, I disagree with using a `try/catch`, because it might mess with the stack trace of the exception (see link in answer). – Daniel Hilgarth Apr 06 '11 at 14:50
  • BTW: The fixed provided by me is the official recommendation by MS to fix this warning. – Daniel Hilgarth Apr 06 '11 at 14:51
  • @DanielHilgarth: There is no "might" in programming; `throw;` does not mess up the stack trace and that's that. As for the official recommendation, I don't think that as professionals we should be blindly "following orders". We can *and should* judge the merits of each approach independently. Anyway, I 'm not advocating the one true solution here; just saying that if readability matters to you as it does to me, `try/catch` might be better. – Jon Apr 06 '11 at 14:54
  • @Jon: **Please** read the link provided, if the exception is thrown in the same method as the try/catch with the throw, it *does* mess with the stack trace! About blindly following orders: I fully agree, but the solution provided is the way that solves the problem without side effects. – Daniel Hilgarth Apr 06 '11 at 14:56
  • @LukeH: The problem is *what happens if the undisposed object doesn't manage to get to the caller*. If it does manage to be returned, then we assume that the caller will dispose of it when it should. – Jon Apr 06 '11 at 14:56
  • @LukeH: You don't seem to understand the reason for this warning. If there is an exception in the method, the caller can't dispose the object, because it never is returned to it... – Daniel Hilgarth Apr 06 '11 at 14:58
  • @Jon: Because readability matters to me, too, I would never implement a method like this. I would either somehow hide it away in utility methods or (more likely) simply ignore this warning. – Daniel Hilgarth Apr 06 '11 at 14:59
  • @DanielHilgarth: I have read the documentation, and didn't see anything related to what you propose. Can you give us a concrete example? – Jon Apr 06 '11 at 14:59
  • @Jon: The code I posted is a concrete example. I quoted the part of the documentation my initial analysis is based on and the example I referred to in my answer is on the documentation page, too. It is the only example on that page that has C# code (The one with the SerialPort). There is no `using` in that example, because it is completely irrelevant to the warning. – Daniel Hilgarth Apr 06 '11 at 15:02
  • @Jon: Good discussion btw! :-) – Daniel Hilgarth Apr 06 '11 at 15:03
  • @Jon, @Daniel: Of course, you're right. Overlooked that one. Although I'd probably use some sort of success flag and check against it in the `finally` rather than swapping `response` and `tmpResponse`. – LukeH Apr 06 '11 at 15:05
  • @DanielHilgarth: Can you give a concrete example of why a solution with `try/catch` (which does not require an extra variable) instead of `try/finally` might cause a problem? See my answer for what exactly I mean. – Jon Apr 06 '11 at 15:07
  • @LukeH: You can do that, sure. But it wouldn't give you any benefits, you still need a second variable. It will basically the same code, you would only save that one line that assigns the tmpResponse to the response. – Daniel Hilgarth Apr 06 '11 at 15:07
  • @Jon: The reason why I disagree with using try/catch is the stack trace of the exceptions. I am not going to explain that problem in this comments, because it is explained in the link I provided. But in short: If an exception is thrown directly inside the try-part of the method, the line number will be the line of the throw statement, not of where the exception originated. – Daniel Hilgarth Apr 06 '11 at 15:10
  • @Daniel: Just that it's more readable, imo. `if (!success) response.Dispose();` is self-describing. Reference swaps and null-checks aren't. – LukeH Apr 06 '11 at 15:10
  • @DanielHilgarth: I am trying to get you to see that what you just said is simply not true. `throw;` (notice the semicolon at the end) will not mess up line numbers and stack traces. – Jon Apr 06 '11 at 15:14
  • @Jon: I know, that you are talking about `throw;` and **not** `throw ex;`. Can you please click on any of the following links and read them? Do me the favor, please: (1) http://weblogs.asp.net/fmarguerie/archive/2008/01/02/rethrowing-exceptions-and-preserving-the-full-call-stack-trace.aspx (2) http://stackoverflow.com/questions/5152265/what-can-lead-throw-to-reset-a-callstack-im-using-throw-not-throw-ex – Daniel Hilgarth Apr 06 '11 at 15:16
  • @DanielHilgarth: Now **that** is what I 've been pestering you about. You could have mentioned it earlier... thanks! – Jon Apr 06 '11 at 15:19
  • @Jon: Erm... I asked you several times to check the second one of those links. Even the question title of that link states that we are talking about `throw;` and not `throw ex;`... – Daniel Hilgarth Apr 06 '11 at 15:21
  • @DanielHilgarth: There's only one doc link in the question body (to MSDN). The other link just redirects to this page. I think that's where we had the communication breakdown. – Jon Apr 06 '11 at 15:25
  • @Jon: There are two links in my answer. The one to MSDN and the one to that question. The text "without messing with the stack trace of your exceptions" is that second link... Is that text linking to MSDN for you? I am confused :-) – Daniel Hilgarth Apr 06 '11 at 15:27
  • @DanielHilgarth: My mistake. I could have *sworn* that link just redirected back to this same question... (no relation to the MSDN link, I read that one fine). Sorry for the trouble. – Jon Apr 06 '11 at 15:32
4

The warning is probably about MyResponse, which is IDisposable.

Why does the warning appear?

If a MyResponse object is constructed, but code later in the method causes an exception to be thrown, then all references to this object will be lost (we only had one, and didn't manage to return it). This means that Dispose cannot be called on the object anymore, and we will be relying on the class finalizer to clean up any resources.

Does it matter?

Generally speaking, it will only matter if:

  • The IDisposable encapsulates a resource that might be needed "soon" by other parts of the program or by another process
  • An exception is thrown before the method returns, to trigger the "problem"
  • That resource is not released by the finalizer soon enough, or for some reason the finalizer never runs but your application doesn't go down

So no, it shouldn't really matter.

How to fix it?

public MyResponse MyMethod(string arg)
{
    MyResponse abc = null;
    try {
        abc = new MyResponse();
        using (Tracer myTracer = new Tracer(Constants.TraceLog))
        {
            // Some code
           return abc;
        }
    }
    catch {
        if (abc != null) {
            abc.Dispose();
        }

        throw;
    }
}

This ensures that if control exits the method by means of an exception, abc is either null or has been properly disposed.

Update

It turns out when using this way of handling things, an exception explicitly thrown from inside MyMethod will be rethrown and have the line number of the first stack frame mutated to point to the throw; statement.

Practically this means that if you have multiple throw statements inside MyResponse, and they throw the same type of exception with the same message, you will not be able to tell which throw was responsible exactly when you catch the exception.

This is IMHO a purely academic problem, but I mention it for completeness.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • I would always dispose of IDisposable objects. There's no guarantee that an IDisposable class even has a finalizer. – TrueWill Apr 06 '11 at 15:06
  • 2
    @TrueWill: So you would ban from your code any method that returns an `IDisposable`? Isn't that taking things a bit too far? Here's one victim of that strategy: http://msdn.microsoft.com/en-us/library/b9skfh7s.aspx. Disposables should be disposed, sure, but only *after* we 're done using them. – Jon Apr 06 '11 at 15:09
  • I would not ban that. Methods can return an IDisposable, but every disposable instance must have an owner (in the sense of something that is responsible for disposing of it). The method contract (probably XML comments) should specify if the caller is responsible for disposing of the returned instance or not. – TrueWill Apr 07 '11 at 23:30
  • BTW: you can skip the ``abc=null`` assignment and ``if(abc!=null)`` check completely and just place ``abc = new MyResponse();`` just before the try block -- without any change (neither loosing exceptions nor disposable instances). – springy76 Oct 02 '11 at 22:35
2

It doesn't really matter. But, contrary to @Aliostad, I think version 2, with the return outside of the using block is better style.

My rationale goes like this:

The using block denotes something that is "opened" and "closed". This is a kind of cheep transaction. Closing the using block says we have done our work and it is safe now to continue with other stuff, like returning.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
0

This warning is probably related to the principle of "single exit point". It is discussed here: http://c2.com/cgi/wiki?SingleFunctionExitPoint

kek
  • 342
  • 1
  • 9