.NET Framework Resource Management
This white paper discusses details of resource management in components written for the Common Language Runtime (CLR).
1. Background. 3
1.1 Garbage Collection. 3
1.2 Finalization. 3
1.2.1 Controlling Finalization Queue. 4
2. Managing Scarce Resources. 5
2.1 Unmanaged Resources. 5
2.2 Managed Resources. 5
2.3 Releasing Unmanaged Resources. 5
2.4 Using IDisposable Types. 6
3. Designing Cleanup Code. 7
3.1 Simple Types. 8
3.2 Finalizable Types. 8
3.3 Disposable Types. 8
3.4 Disposable and Finalizable (Both) Types. 10
3.4.1 Dispose Pattern. 10
3.5 Implementation of Dispose-Time. 11
3.5.1 Suppressing Finalization. 11
3.6 Implementation of Finalize-Time. 12
3.7 Threading Issues. 12
3.8 Versioning. 13
3.9 Inheritance and Resource Management. 13
3.9.1 Inheriting from Both Types 14
3.9.2 Inheriting from Disposable Types 14
3.9.3 Inheriting from Finalizable or Simple Types 15
3.9.4 Finalizer Implementation Details 16
4. Performance Implications. 16
5. Advanced Resource Management Scenarios. 17
5.1 Resource Collector Pattern. 17
5.2 IEnumerable with expensive resources. 17
5.3 Resources Referenced from Unmanaged Memory. 18
1. Background
Historically, developers have had to be very careful when allocating resources that require explicit released. For example in C++, all memory allocated using operator new needs to be released by a call to delete. This has been a source of numerous code defects.
CLR (Common Language Runtime) provides support for automatic memory management that frees developers from the difficult task of managing allocated memory. Unfortunately, memory is not the only resource that needs to be released after use. There are others, including database connections, system handles, etc. These unmanaged resources are not understood by the CLR and may need to be released manually.
Sections 1 and 2 of the document are intended for developers using components that allocate unmanaged resources. The rest of the document is intended mainly for developers implementing such components. If you are only a user of such components, you don’t need to be concerned with most of the issues described below section 2.
The .NET Framework's garbage collector manages the allocation and release of memory for your application. Each time you use the new operator to create an object, the runtime allocates memory for the object from the managed heap. As long as memory space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually, the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.
For the majority of the objects that your application creates, you can rely on the CLR’s garbage collector to automatically perform all the necessary memory management tasks. However, some objects encapsulate unmanaged resources and those resources must be released explicitly. Although the garbage collector is able to track the lifetime of an object that encapsulates an unmanaged resource, it does not have specific knowledge about how to clean up the resource. For these types of objects, the .NET Framework provides the Object.Finalize method, which allows an object to clean up its unmanaged resources properly when the garbage collector reclaims the memory used by the object. By default, the Finalize method does nothing. If you want the garbage collector to perform cleanup operations on your object before it reclaims the object's memory, you must override the Finalize method in your class. You must use destructor syntax for your finalization code in the C# and the C++ with Managed Extensions.
The garbage collector keeps track of objects that have Finalize methods, using an internal structure called the finalization queue. Each time your application creates an object that has a Finalize method, the garbage collector places an entry in the finalization queue that points to that object. The finalization queue contains entries for all the objects in the managed heap that need to have their finalization code called before the garbage collector can reclaim their memory.
Implementing Finalize methods or destructors can have a negative impact on performance, so you should avoid using them unnecessarily. Reclaiming the memory used by objects with Finalize methods requires at least two garbage collections. When the garbage collector performs a collection, it reclaims the memory for inaccessible objects without finalizers. At this time, it cannot collect the inaccessible objects that do have finalizers. Instead, it removes the entries for these objects from the finalization queue and places them in a list of objects marked as ready for finalization. Entries in this list point to the objects in the managed heap that are ready to have their finalization code called. A special runtime thread becomes active and calls the Finalize methods for the objects in this list and then removes the entries from the list. A future garbage collection will determine that the finalized objects are truly garbage because they are no longer pointed to by entries in the list. In this future garbage collection, the objects' memory is actually reclaimed.
The following diagram shows the GC state transition that an object goes through during its lifetime.
1.2.1 Controlling Finalization Queue
The runtime provides two methods to control objects’ eligibility for finalization, GC.SuppressFinalize and GC.ReRegisterForFinalize. GC.SuppressFinalize removes an object from the list of objects that will be finalized when they are no longer reachable, and GC.ReRegisterForFinalize takes an object that was previously suppressed, and adds it back to the list.
GC.SuppressFinalize is used to prevent unnecessary and costly finalization after the object has been cleaned up through other means, for example through IDisposable interface. GC.ReRegisterForFinalize is used to assure resource cleanup after an already cleaned up object reacquires resources that will need to be cleaned up.
In the .NET Framework Beta 2 release, there is a very expensive security check in GC.SuppressFinalize. This check has been removed in the final release, and should not be a source of performance problems.
As we have already mentioned, unmanaged resources are resources that cannot be released by the GC directly. Examples of unmanaged resources include file handles, database connection handles, message queue handles, and native memory allocated using Marshal.AllocHGlobal.
Releasing unmanaged resources requires an explicit call to a custom API, something along the lines of closing a “handle”.
Managed resources are resources that can be released by the GC directly. Examples of managed resources include String, ArrayList, and TimeSpan. Code acquiring managed resources does need to be concerned with releasing them.
Instantiating and calling methods of managed types does not always allocate only managed resources, as it may seem. It may indirectly allocate unmanaged resources.
2.3 Releasing Unmanaged Resources
Any type allocating unmanaged resources, either directly or indirectly, has to provide a mechanism to release the resources. The simplest way of releasing an unmanaged resource is to release it in the Finalizer of the type owning the resource. This approach is perfectly fine for releasing resources that do not have limits on how many you can acquire and do not have a cost associated with keeping them acquired longer than absolutely necessary. Unfortunately, most unmanaged resources are not limitless and not free; rather they are scarce. Database connection is a perfect example of a scarce resource; you can open only a limited number them.
Finalizers are not very good at releasing scarce resources. They run too late. GC starts collection, and runs finalizers, when it detects memory allocation problems. It is not able to detect unmanaged resource allocation problems.
The solution is to provide clients of types that acquire scarce resources with a way to explicitly release the resources. This way the client can release the resources as soon as they are not needed anymore. For example:
try{
MessageQueue queue = new MessageQueue(path);
queue.Send(message);
}
finally{
queue.Dispose(); // explicitly release queue handle
}
To formalize and organize the process of releasing scarce resources, we have defined a couple of design guidelines and the IDisposable interface.
public interface IDisposable {
public void Dispose();
}
This defines a basic interface that declares the Dispose method, which is designed to be a common cleanup routine. When you implement this interface on a type, you are advertising that instances of that type allocate scarce resources. Some scenarios require clients to explicitly release the resources and some don’t. When in doubt, play it safe and always release the resource by making sure Dispose is called after the object is no longer needed.
For example, common implementations of server applications instantiate a new resource in every request, use the resource, and then release it. Such implementations will result in the cycle of allocate->use->release being repeated many times during the lifetime of the application. If the resource is scarce, it is desirable for the cycle to be as short as possible to maximize the number of requests that the application can process per unit of time. Calling Dispose() immediately after the resource is no longer needed will result in maximum throughput of the application.
Also, some multi-user client applications using resources that are shared among users should dispose the resources as soon as possible. This will prevent problems in which some users are denied access to a resource only because another user’s resource is waiting in the finalization queue. Holding a database connection open for longer than needed is a good example of such a programming mistake.
Single user applications, which don’t share scarce resources with other applications, don’t require such urgency in disposing resources. For example, a command line tool that creates a MessageQueue, sends a message, and terminates doesn’t need to explicitly dispose the MessageQueue instance. Please note that if the same code were executed in a server request, it would certainly cause a throughput problem.
Instances of types implementing the IDisposable interface need to be handled very carefully, especially in the presence of possible exceptions. For example, the following code may actually result in the resource not getting disposed
MessageQueue queue = new MessageQueue(“server\\queueName”);
queue.Send(“Hello World”);
queue.Dispose();
If the Send operation throws an exception, the call to Dispose on the following line will not get executed.
A better way to implement the code is to dispose resources in a finally block, which always gets executed, even when an exception is thrown.
MessageQueue queue;
try{
queue = new MessageQueue(“server\\queueName”);
queue.Send(“Hello World”);
}
finally{
queue.Dispose()
}
The codes become more robust but also a bit less readable. The readability problem is even more pronounced in the case of two resources that need to be disposed, since such scenarios require nested try-finally blocks.
MessageQueue source;
MessageQueue destination;
try{
source = new MessageQueue(“server\\sourceQueue”);
Message message = source.Receive();
try{
destination = new MessageQueue(“server\\destinationQueue”);
destination.Send(message);
}
finally{
destination.Dispose();
}
}
finally{
source.Dispose();
}
Unfortunately, the code above lost a lot of its original simplicity. This is why some languages, including C#, introduced the using statement. The statement has semantics similar to the code above without sacrificing simplicity. It expands to multiple try-finally blocks and automatically calls Dispose() on IDisposable objects created inside the using clause.
using(
MessageQueue source = new MessageQueue(“server\\sourceQueue”),
destination = new MessageQueue(“server\\destinationQueue”)
){
Message message = source.Receive();
destination.Send(message);
}
If you work with languages that don’t provide support similar to like the using statement, you should implement the fully expanded code manually.
3. Designing Cleanup Code
When designing a component that allocates scarce resources, you will encounter one of four main variations common to all resource management implementations. You need to identify which variation best fits your component and implement your resource cleanup code accordingly.
This variation applies to types that hold references to only other managed objects that do not implement IDisposable and do not have Finalizers. This includes ensuring that no internal container, for example ArrayList, stores objects that require explicit resource cleanup. In such case, you don’t need to do anything special in terms of resource management.
Example:
public class SimpleType{
ArrayList names = new ArrayList();
…
}
Only types that acquire unmanaged but not scarce resources and zero or more Simple types should implement this variation. For example, a type that allocates only a very small blob of unmanaged memory falls into this category. In such cases having a finalizer ensures that the memory is released but does not oblige clients to call Dispose. This is a rare case and should not be used in most implementations.
Example:
public class FinalizableOnly{
IntPtr nativeMemory = Marshal.AllocHGlobal(4);
~FinalizableOnly(){
Marshal.FreeHGlobal(nativeMemory);
}
…
}
3.3 Disposable Types
Implement this variation for types that allocate, directly or indirectly, only managed resources, and for which most classes derived from the type will also allocate only managed resources. System.Web.UI.Control is an example. The Web Control base class has no unmanaged resources, and most classes derived from it won't either. This is why it does not need a finalizer. However, it is common for such controls to have other managed resources (SqlConnection, MessageQueue, etc.) that implement IDisposable interface, so it implements IDisposable as well to allow these to be cleaned up early.
Example:
public class DisposableOnly: IDisposable{
MessageQueue queue;
bool disposed = false;
public DisposableOnly(string path){
queue = new MessageQueue(path);
}
public virtual void Dispose(){
if(!disposed){
queue.Dispose();
queue = null;
disposed = true;
}
}
public void SendMessage(string message){
if(disposed){
throw new ObjectDisposedException(this.ToString());
}
queue.Send(message);
}
}
After Dispose() is called, objects are free to throw ObjectDisposedException from any instance method except Dispose(). Dispose() can be called multiple times and should never throw ObjectDisposedException. An alternative is to allow an object to be resurrected by reacquiring resources when a method is called on already disposed object. For example:
public class DisposableResurrectableOnly: IDisposable{
MessageQueue queue;
bool disposed = false;
public DisposableOnly(string path){
queue = new MessageQueue(path);
}
public virtual void Dispose(){
if(!disposed){
queue.Dispose();
queue = null;
disposed = true;
}
}
public void SendMessage(string message){
if(disposed){
Ressurect();
}
queue.Send(message);
}
protected void Ressurect(){
queue = new MessageQueue();
disposed = false;
}
}
Some types may want to provide an additional cleanup method with a domain specific name. For example, SqlConnection class provides the method Close. Such method should just call Dispose().
3.4 Disposable and Finalizable (Both) Types
These are types that allocate scarce unmanaged resources. Such types need both the Dispose() method to allow explicit cleanup of the resource and the finalizer as a backup in cases when Dispose() does not get called.
All Both types should declare the following methods and implement the IDisposable interface.
protected virtual void Dispose(bool disposing)
protected void virtual Finalize()
public void Dispose()
The Dispose() method should call Dispose(true) followed by GC.SuppressFinalize only. The finalizer should call Dispose(false) and nothing else. This allows resource management code to be well structured and localized. During development of the .NET Framework, we found such design optimal in terms of readability and maintainability. We believe it helps to minimize the number of code defects in complicated resource management code.
Example:
public class BothType: IDisposable{
public void Dispose(){
Dispose(true);
GC.SupressFinalize(this);
}
~BothType(){
Dispose(false);
}
protected virtual void Dispose(bool disposing){
if(disposing){
…
}
…
}
…
}
The method Dispose(bool) can execute in two distinct scenarios:
- When called directly or indirectly by the user code, the parameter disposing equals true. We will call this scenario “dispose-time”.
- When called by the Common Language Runtime through the finalizer, the parameter disposing equals false. We will call this scenario “finalize-time”.
You need to be careful to implement the scenarios correctly. There are some restrictions on which operations can be executed at finalize-time and which at dispose-time. Sections 3.5 and 3.6, below, describe details of implementing those two scenarios.
Implementing dispose-time code is relatively straightforward. You can touch any kind of managed object or unmanaged resources. There is no difference between what dispose-time code can do and what any other method can do. As you will see in a moment, this is not the case for finalize-time code.
Dispose-time code should call Dispose() on all owned objects that implement the IDisposable interface. By “owned,” I mean objects whose lifetime is solely controlled by the container. In cases where ownership is not as straightforward, techniques such as Handle Collector, described in section 5.15.1, can be used.
Dispose-time code should also set references of all owned objects to null, after disposing them. This will allow the referenced objects to be garbage collected even if not all references to the “parent” are released. It may be a significant memory consumption win if the referenced objects are large, such as big arrays, collections, etc.
protected virtual void Dispose(bool disposing){
// dispose-time code
if(disposing){
queue.Dispose();
queue = null;
}
// finalize-time code
…
disposed = true;
}
Because the cleanup code executed at dispose-time is a superset of the code executed at the finalize-time, there is no need to call the finalize-time code during object finalization after the object has been disposed. Moreover, keeping objects that don’t need to be finalized in the finalization queue has a cost associated with it. This is why the Dispose() method should call GC.SuppressFinalize, which removes the object from the finalization queue and thus prevents unnecessary finalization.
public void Dispose(){
Dispose(true);
GC.SupressFinalize(this);
}
Some types can be used after being disposed. Such types need to call GC.ReRegisterForFinalize when they reacquire resources.
3.6 Implementation of Finalize-Time
Implementing the finalize-time code path is trickier. There are two kinds of objects that cannot be referenced during finalization. Finalizable or Both objects belonging to the same finalization graph must not reference each other in their finalizers. Also, statically referenced Finalizable or Both objects, which normally can be accessed, cannot be accessed when the application domain or the runtime is shutting down.
The order in which objects in a graph are finalized is undefined. This means finalize-time code should not call methods on non-statically referenced Finalizable or Both objects, including the Dispose() method. Such object may already be finalized and may throw when called. Please note that a method on Simple or Disposable object may in turn call a method on one of the objects mentioned above.
Usually, statically referenced objects can be accessed at finalize-time, but not always. If the runtime is shutting down, the GC tries to collect and possibly finalize all objects, regardless of they are still referenced or not. This means that statically referenced objects can be finalized before non-statically referenced ones. In such situations, the finalizer may fail because the statically referenced object may have already been finalized. If you want to access a statically referenced Finalizable or Both object in the finalize-time code path, you should check the value of Environment.HasShutdownStarted. If the value is true, you should not access the static.
protected virtual void Dispose(bool disposing){
// dispose-time code
if(disposing){
…
}
// finalize-time code
CloseHandle();
if(!Environment.HasShutdownStarted){
// the following line would fail if statically referenced items of
// the Debug.Listeners collection are finalized.
Debug.WriteLine(“Finalizer called”);
}
disposed = true;
}
3.83.7 Threading Issues
You should consider whether your cleanup code should be thread-safe or not. The protected Dispose(bool) method cannot be called from both the user thread and the finalizer thread at the same time. It can be called from multiple user threads, though this is not common. Dispose thread-safety issues are no different than thread-safety issues of any other method. If your type is thread safe, make the cleanup code thread safe. If your type is not thread safe, don’t make cleanup code thread-safe either.
Components that are written to be thread safe will often pay a price in performance relative to non-thread-safe components. This is because the locking code or concurrency-friendly algorithms needed to preserve thread safety usually come at a performance cost relative to code which does not need to protect against concurrent access. Knowing that most usage of components is in a single-threaded environment, the designers of the .NET Framework have opted in most cases to leave thread safety enforcement up to the clients.
3.93.8 Versioning
Once you ship a type with a specific resource management support (Simple, Disposable, Finalizable, or Both), there are some significant concerns with versioning to consider. Although adding new resource management support options is a version compatible change, it does introduce some complexity for code that derives from yours. Specifically, if version 1.0 of a type was Disposable, and v 2.0 becomes Both, you now are faced with potentially 3 cleanup methods (Dispose, Dispose(bool), and Finalize) that can be overridden. In addition, the order in which the derived code gets called in relation to the base object changes subtly for each method.
The recommendation is to avoid changing the cleanup type of the object once a version has shipped. It is best to err on the side of more cleanup code than necessary (Both, preferably) than be faced with adding cleanup in the next version of a component.
public class PlayingSafe: IDisposable{
IntPtr nativeMemory = Marshal.AllocHGlobal(4);
public void Dispose(){
Dispose(true);
GC.SupressFinalize(this);
}
~ PlayingSafe(){
Dispose(flase);
}
// Despite the fact that this type may not need to be Disposable,
// it implements the Both variation. This will help derived types
// in implementing clean resource management code.
protected virtual void Dispose(bool disposing){
Marshal.FreeHGlobal(nativeMemory);
}
…
}
3.103.9 Inheritance and Resource Management
When deriving from classes implementing some resource management methods, you should always try to override the method that reflects the highest level of cleanup support the base class offers.
If there is a virtual void Dispose(bool) method on the base class, you are probably inheriting from Both type. In such case, you should override that method for both dispose-time and finalize-time cleanup.
public class DerivedFromBoth: Both{
ManagedResource addedManaged;
NativeResource addedNative;
protected override void Dispose(bool disposing){
try{
// additional dispose-time
if(disposing){
addedManaged.Dispose();
addedManaged = null;
}
// additional finalize-time
CloseHandle(addedNative);
}
finally{
// old dispose and finalize time.
// alternatively, you can not call it bu then this method
// is responsible for cleaning the base class.
base.Dispose(disposing);
}
}
}
You should not override the finalizer or the Dispose() methods, even if they are virtual. Dispose() may be virtual if one of the classes your base class descends from is a Disposable only type.
If only a Dispose() method is present on the base class, you are probably dealing with Disposable type. If you are not adding any support for finalize-time cleanup, the new dispose-time logic should go in an override of the Dispose() method.
public class StillDisposableOnly: DisposableOnly{
public override void Dispose(){
if(!disposed){ // disposed is a field in the base calss
try{
// do additional ceanup here
…
}
finally{
base.Dispose();
}
}
}
}
If you need to add finalize-time cleanup, then implement the Both pattern by providing a protected virtual Dispose(bool) method and call it from Finalize and Dispose with the correct arguments. You must call base.Dispose() from your new Dispose(bool).
public class NowBoth: DisposableOnly{
// Change Dispose variation to Both variation
// Instead of doing cleaup in Dispose(), call Dispose(bool)
public override void Dispose(){
Dispose(true);
// base.Dispose() will call GC.SupressFinalize
}
// added fianlizer
~ NowBoth(){
Dispose(flase);
}
protected virtual void Dispose(bool disposing){
if(disposing){
base.Dispose();
}
// added support for finalize-time cleanup
CloseHandle();
}
}
If no Dispose() method is present, you are deriving from the Simple or Finalizable type. If you don’t plan to add support for dispose-time cleanup, the scenario is trivial; just override the finalizer. If you want to add support for dispose-time cleanup, you must implement IDisposable. However, since the base type doesn't support IDisposable, it may be common for consumers of your class not to call Dispose.
Public class DisposableDerivedFromSimple: SimpleType, IDisposable{
public virtual void Dispose(){
…
}
}
…
class Client{
void Foo(){
SimpleType instance = Factory.Create();
// who and how will call Dispose if Factory.Create returns
// instance of DisposableDerivedFromSimple?
}
}
Another problem with introducing dispose-time logic to classes derived from Finalizable types is that the derived class cannot call GC.SupressFinalize in its implementation of Dispose(). The reason is that this would prevent the base finalize-time code from running because the finalizer of the base class cannot be called explicitly in Dispose(bool).
public class BothFromFinalizable: FinalizableOnly, IDisposable{
public void Dispose(){
Dispose(true);
// cannot call GC.SupressFinalize!!!
}
~ BothFromFinalizable(){
Dispose(flase);
}
protected virtual void Dispose(bool disposing){
if(disposing){
base.Dispose();
}
// how would I call base.Fianlize()?!!!
}
}
Because of the problems discussed above, you should consider whether inheriting from the type can be avoided. We recommend that you use other design alternatives such as containment, delegation, etc., instead of deriving disposable type form a non-disposable base class.
To facilitate the four versions of cleanup logic that types may offer, C# and other languages have introduced changes into the languages that make cleanup easier. For example C# generates the following code when you add a finalizer to your type.
protected override void Finalize() {
try {
// body of your destructor goes here
}
finally {
base.Finalize();
}
}
This ensures that the base class finalization code will be called in the case of Finalizable types, but it does not help Both types. This same code is generated for all destructors, as C# doesn't check whether the object is a Both type.
When Dispose is not called on a Both object, the object is allowed to finalize. This is fairly expensive. The GC must do extra bookkeeping work to invoke the finalize method. Also, some applications have many threads creating objects, and because only one thread can be executing the finalizers, the finalization thread may have problems handling large numbers of objects. This is why creating many objects with Finalize methods on different threads and letting them to get finalized can lead to poor performance.
Because of this, you shouldn't add a Finalize method to a type unless the type allocates unmanaged resources or you anticipate most derived types having to allocate unmanaged resources. However, if you do decide to implement a Finalize method, it is highly recommended that you implement the Both pattern. This will allow users to call Dispose and avoid having the Finalize method invoked.
System.ComponentModel.Component implements the Both variation. The type does not allocate unmanaged resources itself, but we expected most types derived from Component to allocate such resources. Types that don’t want to take the performance hit associated with the pattern should implement IComponent instead of inheriting from Component.
5. Advanced Resource Management Scenarios
There are scenarios in which the ownership of Disposable or Both objects is not well defined. For example, the reference to an object may be handed off to multiple clients. In such scenarios, it is very difficult, if not impossible, to dispose the scarce resource as soon as it is no longer needed. None of the clients knows when the other clients are done with the object.
One set of solutions to the problem relies on letting the finalizer collect the resources and just forcing the GC to collect more often than it normally would. We call such solutions Resource Collectors. Resource Collectors helps to minimize the time between when the resource is available for collection and the time when the finalizer actually runs and releases the resource. One drawback of such an approach is that forcing collection, if abused, may actually lead to performance loss. Good implementations of the pattern employ clever algorithms determining when forced collection should be executed.
One such implementation, a component called HandleCollector, can be downloaded from the GotDotNet site (http://GotDotNet). The collector maintains a counter of “live” resources. The counter is incremented when a resource is allocated and is decreased when the resource is released by the finalizer. When a new resource is about to be allocated and the counter is greater than an application specified threshold, collection is forced by a call to GC.Collect.
A degenerated version of a Resource Collector implementation uses a timer to force collection at specified intervals. Such an implementation is discouraged. The implementation described above is much better at forcing collection when needed and avoiding unnecessary collections which may significantly impair performance and scalability.
Often IEnumerable types allocate scarce resources in GetEnumerator(). For example, the IEnumerator returned from GetEnumerator may hold a reference to an expensive array of Disposable objects. Such IEnumerator types should implement the IDisposable interface to allow explicit cleanup.
internal class ExpensiveEnumerator: IEnumerator, IDisposable{
…
}
public class ExpensiveCollection: IEnumerable{
public IEnumerator GetEnumerator(){
return new ExpensiveEnumerator();
}
…
}
Some languages, for example C# and VB.NET, will call Dispose on enumerators that implement IDisposable when the foreach statement completes.
ExpensiveCollection expensive = new ExpensiveCollection();
foreach(ExpensiveItem in expensiveCollection){
…
} // Dispose will be called on the IEnumerator when the loop terminates
There are situations in which an object in unmanaged memory holds a reference to a managed object. Such managed objects cannot be collected, even when there are no managed references to them, without an explicit call freeing the last reference from the unmanaged object. The problem can be solved by containing the object that needs to be referenced from unmanaged memory inside a wrapper. The internal object is never handed off to the managed client code. The wrapper is, and when disposed or finalized, frees the reference from the unmanaged object.