4

Consider the following:

        TextReader reader = new StreamReader(file);
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        return (T)xmlSerializer.Deserialize(reader);

And

        using (TextReader reader = new StreamReader(file))
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            return (T)xmlSerializer.Deserialize(reader);
        }

What will actually happen in the latter piece of code? Will the Dispose() be called?

kasperhj
  • 10,052
  • 21
  • 63
  • 106

5 Answers5

4

Yes it will be called.

The using statement is syntactic sugar for:

try
{
    // Do stuff
    return;
}
finally
{
    // Dispose
}

and the finally will get called even on the return.

So you are safe to use this.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
3

Yes, the Dispose will be called.

Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • I'll add that probably there are some small caveats: http://stackoverflow.com/questions/3216046/does-the-c-finally-block-always-execute/3216078#3216078 But these are very exceptional situations, so the OP can ignore them. – xanatos Aug 31 '11 at 09:17
1

Yes, this is how it will be converted in IL.

   TextReader reader;
    T returnValue;
    try{
       reader = new StreamReader(file));   
       XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));   
       var obj = xmlSerializer.Deserialize(reader);   
       returnVal = (T)obj;   
    }
    finally{        
         reader.Dispose();
         return returnVal;              
    }
Manish Basantani
  • 16,931
  • 22
  • 71
  • 103
  • This differs from the other answers, though it makes the most sense in that the return is completed last, as I would expect. Is it certain it's like that, and do the other answers hide this truth? – kasperhj Aug 31 '11 at 09:26
  • I love the fact that you didn't even bother renaming the variables when you copy-pasted. you're code doesn't compile however – Rune FS Aug 31 '11 at 10:53
1

The resource of the using statement, reader will be disposed when the using scope ends. In you're case that's when the result of the deserialization has been casted to T.

you could extend you're code to the (roughly) equivalent below:

TextReader reader = null;
try{
  reader = new StreamReader(file);
  XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
  var obj = xmlSerializer.Deserialize(reader);
  T returnVal = (T)obj;
  return returnVal;
} finally{
   reader.Dispose();
}

in that version it becomes clear that the last time reader is used is way before the return statement.

If you were to return reader you would run into problems since the object returned would be disposed and hence unusable.

EDIT: The IL of the above code is:

IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  stloc.0
  .try
  {
    IL_0003:  nop
    IL_0004:  ldstr      ""
    IL_0009:  newobj     instance void [mscorlib]System.IO.StreamReader::.ctor(string)
    IL_000e:  stloc.0
    IL_000f:  ldtoken    !!T
    IL_0014:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0019:  newobj     instance void [System.Xml]System.Xml.Serialization.XmlSerializer::.ctor(class [mscorlib]System.Type)
    IL_001e:  stloc.1
    IL_001f:  ldloc.1
    IL_0020:  ldloc.0
    IL_0021:  callvirt   instance object [System.Xml]System.Xml.Serialization.XmlSerializer::Deserialize(class [mscorlib]System.IO.TextReader)
    IL_0026:  stloc.2
    IL_0027:  ldloc.2
    IL_0028:  unbox.any  !!T
    IL_002d:  stloc.3
    IL_002e:  ldloc.3
    IL_002f:  stloc.s    CS$1$0000
    IL_0031:  leave.s    IL_003d
  }  // end .try
  finally
  {
    IL_0033:  nop
    IL_0034:  ldloc.0
    IL_0035:  callvirt   instance void [mscorlib]System.IO.TextReader::Dispose()
    IL_003a:  nop
    IL_003b:  nop
    IL_003c:  endfinally
  }  // end handler
  IL_003d:  nop
  IL_003e:  ldloc.s    CS$1$0000
  IL_0040:  ret
} // end of method

the thing to notice is that CS$1$0000 which is the return value is push to the stack just before the only ret instruction. So the order of execution is different from what it looks like in C# code. Further it's worth noting the stloc.s CS$1$0000 and leave.s instrcutions which stores the return value followed by one of the glorified GOTOs. leave.s leaves the try and jumps to the label IL_003d, just before pushing the return value to the stack

Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • Doesn't `return` leave the scope, without ever invoking the Dispose()? – kasperhj Aug 31 '11 at 09:27
  • @lejon the finally block will always be execcuted. In reality there's only ever on return point from a method when looking at the IL. multiple return statements and finally blocks are basically glorified GOTOs – Rune FS Aug 31 '11 at 09:34
  • So, Amby's answer is more like the IL that you mention? – kasperhj Aug 31 '11 at 10:41
  • @Lejon not really. as you can see from my edit there's no return from the final block in the IL. There's only one return and that's after both the try block and the finallly block and both obj and xmlSerializer is declared outside of the try scope. (Declaration scope is a language thing more than an IL thing) – Rune FS Aug 31 '11 at 11:04
0

Yes, see here:-

http://msdn.microsoft.com/en-us/library/yh598w02(VS.80).aspx

Lloyd
  • 29,197
  • 4
  • 84
  • 98