In general, the answer to the question of whether or not to use inheritance or an interface can be answered by thinking about it this way:
When thinking about hypothetical
implementing classes, is it a case
where these types are what I'm
describing, or is it a case where
these types can be or can do what I'm
describing?
Consider, for example, the IEnumerable<T>
interface. The classes that implement IEnumerable<T>
are all different classes. They can be an enumerable structure, but they're fundamentally something else (a List<T>
or a Dictionary<TKey, TValue>
or a query, etc.)
On the other hand, look at the System.IO.Stream
class. While the classes that inherit from that abstract class are different (FileStream
vs. NetworkStream
, for example), they are both fundamentally streams--just different kinds. The stream functionality is at the core of what defines these types, versus just describing a portion of the type or a set of behaviors that they provide.
Often you'll find it beneficial to do both; define an interface that defines your behavior, then an abstract class that implements it and provides core functionality. This will allow you to, if appropriate, have the best of both worlds: an abstract class for inheriting from when the functionality is core, and an interface to implement when it isn't.
Also, bear in mind that it's still possible to provide some core functionality on an interface through the use of extension methods. While this doesn't, strictly speaking, put any actual instance code on the interface (since that's impossible), you can mimic it. This is how the LINQ-to-Objects query functions work on IEnumerable<T>
, by way of the static Enumerable
class that defines the extension methods used for querying generic IEnumerable<T>
instances.
As a side note, you don't need to throw any NotImplementedException
s. If you define a function or property as abstract
, then you don't need to (and, in fact, cannot) provide a function body for it within the abstract class; the inheriting classes will be forced to provide a method body. They might throw such an exception, but that's not something you need to worry about (and is true of interfaces as well).