Dispose pattern: Difference between revisions

Content deleted Content added
Problems: Init, give balanced view of problems
Line 138:
} // try
</source>
 
== Problems ==
Beyond the key problem of correct resource management in the presence of returns and exceptions, and heap-based resource management (disposing objects in a different scope from where they are created), there are many further complexities associated with the dispose pattern. These problems are largely avoided by [[RAII]]. However, in common simple use these complexities do not aris: acquire a single resource, do something with it, automatically release it.
 
A fundamental problem is that having a resource is no longer a [[class invariant]] (the resource is held from object creation until it is disposed, but the object is still live at this point), so the resource may not be available when the object tries to use it, for example trying to read from a closed file. This means that all methods on the object that use the resource potentially fail, concretely usually by returning an error or raising an exception. In practice this is minor, as use of resources can usually fail for other reasons as well (for example, trying to read past the end of a file), so these methods already might fail, and not having a resource just adds another possible failure. A standard way to implement this is to add a boolean field to the object, called <code>disposed</code>, which is set to true by <code>dispose</code>, and checked by a [[guard clause]] to all methods (that use the resource), raising an exception (such as <code>ObjectDisposedException</code> in .NET) if the object has been disposed.<ref name="msdn_dispose">{{cite web |url=https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx |title=Dispose Pattern}}</ref>
 
Further, it is possible to call <code>dispose</code> on an object more than once. While this may indicate a programming error (each object holding a resource must be disposed ''exactly'' once), it is simpler, more robust, and thus usually preferable for <code>dispose</code> to be [[idempotent]] (meaning "calling multiple times is the same as calling once").<ref name="msdn_dispose"/> This is easily implemented by using the same boolean <code>disposed</code> field and checking it in a guard clause at the start of <code>dispose</code>, in that case returning immediately, rather than raising an exception.<ref name="msdn_dispose"/> Java distinguishes disposable types (those that implement [https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html AutoCloseable]) from disposable types where dispose is idempotent (the subtype [https://docs.oracle.com/javase/8/docs/api/java/io/Closeable.html Closeable]).
 
Disposal in the presence of inheritance and composition of objects that hold resources have analogous problems to destruction/finalization (via destructors or finalizers). Further, since the dispose pattern usually does not have language support for this, [[boilerplate code]] is necessary. Firstly, if a derived class overrides a <code>dispose</code> method in the base class, the overriding method in the derived class generally needs to call the <code>dispose</code> method in the base class, in order to properly release resources held in the base. Secondly, if an object has a "has a" relationship with another object that holds a resource (i.e., if an object indirectly uses a resource through another object that directly uses a resource), should the indirectly using object be disposable? This corresponds to whether the relationship is ''owning'' ([[object composition]]) or ''viewing'' ([[object aggregation]]), or even just ''communicating'' ([[association (object-oriented programming)|association]]), and both conventions are found (indirect user is responsible for the resource or is not responsible). If the indirect use is responsible for the resource, it must be disposable, and dispose the owned objects when it is disposed (analogous to destroying or finalizing owned objects).
 
Composition (owning) provides [[Encapsulation (computer programming)|encapsulation]] (only the object that is used needs to be tracked), but at the cost of considerable complexity when there are further relationships between objects, while aggregation (viewing) is considerably simpler, at the cost of lacking encapsulation. In [[.NET]], convention is to only have direct user of resources be responsible: "You should implement IDisposable only if your type uses unmanaged resources directly."<ref name="idisposable">{{cite web |url=https://msdn.microsoft.com/en-us/library/system.idisposable(v=vs.110).aspx |title=IDisposable Interface |accessdate=2016-04-03}}</ref> See [[Resource management (computing)|resource management]] for details, and further examples.
 
== See also ==