Dispose on an IList?


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


 


 


 


 

Comments (5)

  1. Chris Nahr says:

    Maybe I don’t understand the issue but are you sure you answered the question?

    I think you talked about disposing the _enumerator_ while the question was disposing of the _elements_ of the list. Enumerators don’t touch the elements at all. Neither does yours, as far as I can tell.

    If you were thinking about disposing of elements in the Dispose method of an enumerator… well, that’s code obfuscation on a C++ level if you ask me. 🙂 The foreach loop proposed by the reader should do the job just fine.

  2. Brad Abrams says:

    Yea, you are right… I said it was a parital answer… I wounder if you can restructure the problem such that the enumerator holds the resources that need to be freed…

  3. Chris Nahr says:

    Not sure you could do anything (sensible) with the enumerator. What I would do is create a wrapper object that can be disposed of wholesale, thereby disposing of all the elements in the wrapped collection. That way you could use a single using statement, just like when you’re opening a file or showing a dialog.