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
.