You can open any C# tutorial and you’ll find some information about abstract
classes and interfaces. Most likely, you’ll not find any information about what is the difference between abstract class and interface.
This theme was discussed earlier on the Internet several times but I want to consolidate all the most important thoughts regarding the problem of choosing between an abstract
class and interface
in this post.
Let’s consider the mechanical and semantic difference between abstract
classes and interface
s.
What is an abstract
class and what is an interface
? The main mechanical difference is that an abstract
class can have a default implementation, whereas an interface
is just a bunch of member declarations, it defines their signatures. Here is a short example:
public abstract class AlgorithmBase {
public int Do() {
int a1 = Operation1();
int a2 = Operation2();
return a1 + a2;
}
public virtual int Operation1() {
return 55 ^ 35;
}
public virtual int Operation2() {
return 21 + 48;
}
}
public interface Algorithm {
int Operation1();
int Operation2();
}
Also, you can’t create an instance of an abstract
class. So, the mechanical difference is pretty obvious for any meaningful C# developer. Let’s talk about the semantic payload of abstract
classes and interface
s.
Very often, you can hear that interface
s define contracts. This statement becomes more convincing with the fact that in WCF, we treat interface
s and contracts equally. In WCF, a service contract can only be represented by an interface
.
In the real world, including the real world outside of programming, contracts have some semantic payload. Usually, they determine some kind of relationships between people, rights, objects and so on. Interface
s do not have any semantic payload. They determine nothing except signatures. But signatures don’t bear any significant semantic payload. An interface
represents just a shape. Thus, interface
s are not contracts. Here is an example of a contract provided by Krzysztof Cwalina:
public abstract class CollectionContract<T> : IList<T> {
public void Add(T item) {
AddCore(item);
count++;
}
public int Count {
get { return count; }
}
protected abstract void AddCore(T item);
private int count;
...
}
This contract says that when an item is added to the collection, the Count
property is incremented by one. In addition, this contract is locked for all subtypes.
Interface
s are made of stone. They can’t be easily changed without breaking existing clients. At the same time, interface
s are easily extendable by clients. A client can extend an interface
by extension methods and by the way, if a client wants to implement an interface
on a class which already inherits from another class, a client can easily do that, a client couldn’t do that with an abstract
class instead of an interface
, since multiple inheritance in C# is deprecated.
So, in the end, an interface is more supple from the client’s perspective: any class can implement as many interfaces as it wants to. Unfortunately, an interface is more rigid from the developer’s perspective: it can’t be easily changed and it does not support any kind of reusability.
An abstract class is supple from the developer’s perspective: it supports reusability, it supports encapsulation, it can be extended easily without breaking existing clients.
With all that said, we can conclude the interesting rule of thumb: use abstract
classes for building internal APIs and use interface
s for providing external points of extension. Remember, this is not a dogma, this is a rule of thumb.
Look at an example from an open-source project.
public abstract class BitMatrix {
public abstract bool this[int i, int j] { get; set; }
public abstract int Width { get; }
public abstract int Height { get; }
internal MatrixSize Size {
get { return new MatrixSize(Width, Height); }
}
internal bool this[MatrixPoint point] {
get { return this[point.X, point.Y]; }
set { this[point.X, point.Y] = value; }
}
internal void CopyTo(SimpleBitMatrix target,
MatrixRectangle sourceArea, MatrixPoint targetPoint) {
for (int j = 0; j < sourceArea.Size.Height; j++) {
for (int i = 0; i < sourceArea.Size.Width; i++) {
bool value = this[sourceArea.Location.X + i, sourceArea.Location.Y + j];
target[targetPoint.X + i, targetPoint.Y + j] = value;
}
}
}
internal void CopyTo(SimpleBitMatrix target, MatrixPoint targetPoint) {
CopyTo(target, new MatrixRectangle(new MatrixPoint(0, 0),
new MatrixSize(Width, Height)), targetPoint);
}
}
public abstract class SquareBitMatrix : BitMatrix {
private readonly int m_Width;
protected SquareBitMatrix(int width) {
m_Width = width;
}
internal static int GetWidthByVersion(int version) {
return 17 + 4 * version;
}
public override int Height {
get { return Width; }
}
public override int Width {
get { return m_Width; }
}
}
The BitMatrix
is an abstract
class which exposes a lot (not so much in fact, but considerable amount) of reusable code. The SquareBitMatrix
overrides the Width
and Height
and reuses the base class logic.
I’ll not provide code examples of interfaces from BCL, just recall that BCL provides ICollection
, IList
, INotifyPropertyChanged
and tons of other interface
s. This is done so because the extensibility from the client’s perspective is more important in these cases.