225

Something like:

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

I believe it is not a proper place for a return statement, is it?

tafa
  • 7,146
  • 3
  • 36
  • 40

7 Answers7

220

As several others have pointed out in general this is not a problem.

The only case it will cause you issues is if you return in the middle of a using statement and additionally return the in using variable. But then again, this would also cause you issues even if you didn't return and simply kept a reference to a variable.

using ( var x = new Something() ) { 
  // not a good idea
  return x;
}

Just as bad

Something y;
using ( var x = new Something() ) {
  y = x;
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    Just I was about to edit my question about the point you mentioned. Thanks. – tafa Mar 19 '09 at 16:05
  • Please help me understand why this is bad. I would like to return a Stream that I am using in a helper function to another function for image processing. It seems like the Stream will be disposed if I do this? – John Shedletsky Mar 05 '15 at 23:30
  • 4
    @JohnShedletsky In this case your function call should be wrapped with using. Like using(Stream x = FuncToReturnStream()){...} and no using inside FuncToReturnStream. – Felix Keil Sep 24 '15 at 12:52
  • @JohnShedletsky I'm sure it's because `return` statement makes the end of the `using` block inaccessible by any code paths. The end of the `using` block needs to be ran so the object can be disposed if needed. – mekb Jul 22 '19 at 11:21
169

It's perfectly fine.

You are apparently thinking that

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

is blindly translated into:

IDisposable disposable = GetSomeDisposable()
//.....
//......
return Stg();
disposable.Dispose();

Which, admittedly, would be a problem, and would make the using statement rather pointless --- which is why that's not what it does.

The compiler makes sure that the object is disposed before control leaves the block -- regardless of how it leaves the block.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • Great answer @James Curran! But it makes me rather curious what it IS translated into. Or is that only expressible in IL ? (which I have never really tried reading before). – Bart Jan 21 '16 at 22:36
  • 1
    @Bart - I think of it as evaluating the return expression into a temporary variable, then doing the dispose, then returning the temporary variable. – ToolmakerSteve Jan 16 '17 at 18:23
  • @James Curran. From top to here, Only you explained what happed in the background. Many thanks. – Sercan Timoçin Sep 10 '19 at 15:18
  • 1
    @Bart it's probably translated to: try { ...your code... } finally { x.Dispose(); } – Bip901 Feb 07 '20 at 14:36
  • Related, a nice workaround I found for working with `IDisposable`s that I need to "use" after the function returns is to pass in a `Func`. For example, I have some helpers that create connections and execute queries, but I can pass in a `Func` to extract the result. My code only needs one set of `using` blocks and my other functions focus on setting up the queries and parameters. – Adam Jul 03 '23 at 16:51
104

It's absolutely fine - no problem at all. Why do you believe it's wrong?

A using statement is just syntactic sugar for a try/finally block, and as Grzenio says it's fine to return from a try block too.

The return expression will be evaluated, then the finally block will be executed, then the method will return.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
29

This will work perfectly fine, just as returning in the middle of try{}finally{}

Grzenio
  • 35,875
  • 47
  • 158
  • 240
19

The code bellow shows how using is working:

private class TestClass : IDisposable
{
   private readonly string id;

   public TestClass(string id)
   {
      Console.WriteLine("'{0}' is created.", id);
      this.id = id;
   }

   public void Dispose()
   {
      Console.WriteLine("'{0}' is disposed.", id);
   }

   public override string ToString()
   {
      return id;
   }
}

private static TestClass TestUsingClose()
{
   using (var t1 = new TestClass("t1"))
   {
      using (var t2 = new TestClass("t2"))
      {
         using (var t3 = new TestClass("t3"))
         {
            return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3));
         }
      }
   }
}

[TestMethod]
public void Test()
{
   Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString());
}

Output:

't1' is created.
't2' is created.
't3' is created.
'Created from t1, t2, t3' is created.
't3' is disposed.
't2' is disposed.
't1' is disposed.

The disposed are called after the return statement but before the exit of the function.

Bertrand
  • 199
  • 1
  • 2
  • 1
    Please note that some C# objects dispose in a custom fashion, for example, WCF clients is a using statement like above return "can't access disposed object" – OzBob Feb 22 '19 at 01:20
19

That is totally acceptable. A using statement ensures the IDisposable object will be disposed no matter what.

From MSDN:

The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler.

mbillard
  • 38,386
  • 18
  • 74
  • 98
-4

Perhaps it isn't 100% true that this is acceptable...

If you happen to be nesting usings and returning from within a nested one, it might not be safe.

Take this as an example:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
            return memoryStream.ToArray();
        }
    }
}

I was passing in a DataTable to be outputted as csv. With the return in the middle, it was writing all the rows to the stream, but the outputted csv was always missing a row (or multiple, depending on the size of the buffer). This told me that something wasn't being closed properly.

The correct way is to make sure all the previous usings are disposed properly:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
        }
    }

    return memoryStream.ToArray();
}
Bugs
  • 4,491
  • 9
  • 32
  • 41
yeahright
  • 40
  • 4