A developer on an internal alias asks:

 

I’m using an API which returns an IList of objects which have to be Disposed.  Are there any pitfalls I should be aware of, or can I just use a foreach like the following?

 

foreach (Thing myThing in myList)

{

       myThing.Dispose();

}

 

I gave her a partial answer… I hope you will find it helpful as well. The full code is for the project is here

 

If you make the enumerator returned by Thing.GetEnumerator() implement IDisposable, then the compiler (C# or VB.NET) will automatically call Dispose() for you…

 

For example, the following lines of C# code:

 

    foreach (string s in l) {

       Console.WriteLine(s);

    }

 

The dispose method on l’s enumerator will be called when the foreach is completely executed.

 

For the geeks, here is the generate this IL…  comments mine

 

 

//Fist we get your enumerator, this is the class that needs to implement IDisposable()… in our case it is a generic one (notice the arity notation `1… but this works fine with non-generics as well.  

IL_0037:  callvirt   instance class [mscorlib]System.Collections.Generic.'IEnumerator`1'<!0> class ConsoleApplication1.'MyList`1'<string>::GetEnumerator()

IL_003c:  stloc.2

.try

//Notice it is all in a try block, this is part of the magic that ensures that the even if the body throws an exception, dispose still gets executed.  

  {

    IL_003d:  br.s       IL_004e

    IL_003f:  ldloc.2

    IL_0040:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.'IEnumerator`1'<string>::get_Current()

    IL_0045:  stloc.1

    IL_0046:  ldloc.1

    IL_0047:  call       void [mscorlib]System.Console::WriteLine(string)

    IL_004c:  nop

    IL_004d:  nop

    IL_004e:  ldloc.2

    IL_004f:  callvirt   instance bool class [mscorlib]System.Collections.Generic.'IEnumerator`1'<string>::MoveNext()

    IL_0054:  stloc.3

    IL_0055:  ldloc.3

    IL_0056:  brtrue.s   IL_003f

//When there are no more items in the list MoveNext() return false, and we jump outside the block, causing the finally block to be executed…

    IL_0058:  leave.s    IL_006a

  }  // end .try

  finally

  {

    IL_005a:  ldloc.2

    IL_005b:  ldnull

    IL_005c:  ceq

    IL_005e:  stloc.3

    IL_005f:  ldloc.3

    IL_0060:  brtrue.s   IL_0069
//First we check to see if the enumerator is not null (it would be bad to throw an exception from the finally block)

    IL_0062:  ldloc.2

//Then we call Dispose();

    IL_0063:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()

    IL_0068:  nop

    IL_0069:  endfinally

  }  // end handler