3

Suppose I have an interface with two concrete classes. One concrete needs to implement IDisposable. Should the interface be amended to implement IDisposable for the benefit of one class or should the consumer of the interface have to perform runtime checks for disposability?

I assume the interface should be amended as it is a simple change (especially if its a new interface) but I also can see a possible violation of liskov in changing a design to suit a particular implementation (especially if the other class or classes have to throw not supported exceptions)

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Martin Bliss
  • 1,043
  • 7
  • 24
  • Why not just have the concrete class that needs `IDisposable` implement it as an additional interface? – Tim May 14 '13 at 02:10
  • Interesting question. Garbage needs to be exposed accordingly by the owner of that garbage or things will get very messy. If the interface were to be the owner of the disposable resource then yes, the interface should implement IDisposable however since its an interface its never really not clear who's going to be the owner until a concrete implementation comes along. So I'd say no, rather the concrete class should implement IDisposable or 'borrow' its concrete disposable member from a higher owner. – Polity May 14 '13 at 02:34
  • I recommend reading ["An Autofac lifetime primer" by N. Blumhardt](http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/), esp. the section "IDisposable and ownership", which goes into exactly this issue. – stakx - no longer contributing May 18 '13 at 22:38
  • @stakx Thanks for the tip! Very interesting read! – Martin Bliss May 20 '13 at 20:48
  • @Tim : Because the consumer only knows about the concrete via an interface (they're loosely coupled.) Therefor, the consumer doesn't know the instance requires IDisposable, and thus, cannot call IDisposable.Dispose() without a runtime cast or reflections, both of which violate Liskov. – Martin Bliss May 30 '13 at 22:10

2 Answers2

1

If the framework itself is any indication, the appropriateness of having the interface implement IDisposable depends on whether disposability is a necessary property to fulfill the contract the interface defines. A small number of Framework interfaces do implement IDisposable, including:

System.Collections.Generic.IEnumerator<T>
System.Deployment.Internal.Isolation.Store
System.Resources.IResourceReader
System.Resources.IResourceWriter
System.Security.Cryptography.ICryptoTransform
System.ComponentModel.IComponent
System.ComponentModel.IContainer

By their nature, these interfaces generally define constructs that will consume, and thus need to release, a resource. In that sense, disposing of resources could be considered an integral part of the implementation contract, rather than an implementation detail of concrete classes that implement the interface. For instance, an IResourceReader will read from a resource, and closing the resource is a necessary part of the implementation contract.

In contrast, it is very common in the Framework where concrete classes implement IDisposable directly (not through another interface). For framework classes, this can be queried through reflection:

foreach (var v in typeof(/*any type*/)
                      .Assembly.GetTypes()
                      .Where(a => a.IsClass 
                              && typeof(IDisposable).IsAssignableFrom(a)
                              && a.GetInterfaces().Where(
                               i=>i!=typeof(IDisposable)
                       ).All(i=>!typeof(IDisposable).IsAssignableFrom(i))))
{
   foreach (var s in v.GetInterfaces())
       Console.WriteLine(v.FullName + ":" + s.Name);
}

Generally, these are classes whose implementation requires the consumption of resources, incidental to fulfilling the interface contract. For instance, System.Data.SqlClient.SqlDataAdapter implements IDbDataAdapter and IDisposable separately; it is fully possible for an IDbDataAdapter to not require disposition, but the implementation of SqlDataAdapter requires the consumption and release of resources.

In your case, you indicate that there are two classes that implement your interface, one that needs to implement IDisposable, and one which does not. Given that one does not, the ability to dispose of resources is by definition not integral to fulfilling the requirements of the interface; it follows that the interface itself should not implement IDisposable.

Incidentally, Dispose() should not throw an exception (see CA1065: Do not raise exceptions in unexpected locations.) If a class instance that implements IDisposable has no resources to dispose, it can simply return; the postcondition that all resources are released is satisfied. It is not necessary to throw a NotSupportedException.

Addendum

A second potential consideration is the anticipated usage of the interface. For instance, it is common to use the following pattern in database scenarios:

 System.Data.IDbCommand cmd = ...;
 using (var rdr = cmd.ExecuteReader()) // returns IDataReader (IDisposable)
 {
     while (rdr.Read()) {...}
 } // dispose

If IDataReader were not to implement IDisposable, the equivalent code would need to be significantly more complex:

 System.Data.IDbCommand cmd = ...;
 System.Data.IDataReader rdr;
 try
 {
     rdr = cmd.ExecuteReader();
     while (rdr.Read()) {...};
 } finally {
     if (rdr is IDisposable) ((IDisposable)rdr).Dispose();
 }

If this type of usage is expected to be common, it may justify making the interface IDisposable as a special case, even if not all implementations will be expected to implement IDisposable.

drf
  • 8,461
  • 32
  • 50
  • 1
    The fact that `using` cannot perform a runtime check is a strong argument in favor of making almost every interface inherit from `IDisposable`. – Ben Voigt May 14 '13 at 04:09
  • In the case that a concrete implements disposable (or any interface needing consumer follow up) how could one deal with the inevitable design orphanage that occurs wirhout he consumer having to perform unreasonable constant runtime checks? – Martin Bliss May 14 '13 at 21:25
  • In general, having to perform constant runtime checks would be emblematic of a design problem; the consumer of the interface should be agnostic to its implementation. But `IDisposable` is a special case, since it represents a technical constraint on the implementation, an artifact of a [leaky abstraction](http://www.joelonsoftware.com/articles/LeakyAbstractions.html). I suggest reading the responses to [this question](http://stackoverflow.com/questions/15707054/interfaces-using-idisposable) on this specific case. – drf May 15 '13 at 02:40
1

I found the answer while reading Mark Seemann's book on Dependency Injection. IDisposable on an interface is automatically a leaky abstraction since IDisposable is an implementation detail ONLY. That said, not all interfaces are abstractions and thus - in the name of programming strictly to interfaces - there will be situations where interfaces must implement IDisposable.

Though it is much preferred that a concrete implement IDisposable than an interface, in both cases the solution is to create a coarse-grained abstraction over the resource. Each method implementation of the abstraction would then create and dispose the resource, relieving the consumer of the burden of doing the same. I like this approach because it reduces the complexity of lifetime management on the part of a consumer (which really should be none, especially in DI.)

In order to implement DI in the above scenario, you will probably need to inject a factory, allowing each method to instantiate the dependency ad-hoc.

Martin Bliss
  • 1,043
  • 7
  • 24