Generics Constraints: Use Interfaces over Base Class
Prologue:
I’m a big fan of Interface with a Base Class which implement that interface with base behavior. Providing this two together allows the programmer to decide if he wants to inherit from the Base Class and override only the things he really wants or to implement the Interface from scratch.
Here is an example from our code:
1). The Interface:
public interface IPersistentEntity
{
string ToXml();
string GetKey();
// … snippet …
}
2). The Base Class that implements the interface:
/// <summary>
/// Base class for a Business Entity.
/// </summary>
public abstract class EntityBase : IPersistentEntity
{
public string GetKey()
{
return GetKeyCore();
}
protected abstract string GetKeyCore();
public virtual string ToXml()
{
// Build the xml via Reflection over the current type.
}
// … snippet …
}
I constraint my EntityCollection object to EntityBase:
/// <summary>
/// Represents a strongly typed list of Business Entities.
/// </summary>
/// <typeparam name=”ENT”>Entity type which inherits from EntityBase class.</typeparam>
[Serializable]
public class EntityCollection<ENT> : List<ENT>, ISerializable
where ENT : EntityBase
{
// … snippet …
}
As you can tell, I’m constraining the ENT generic type to be any object that inherit from EntityBase.
The Story:
My teammate, Moran (Yes, him again, goshh… he really challenges me to think deeply about our infrastructure), wanted to create a new EntityCollection of ISpecificEntity (i.e: EntityCollection<ISpecificEntity>). That means that he wanted to extend the IPersistantEntity I showed above.
The problem was that my EntityCollection made a constraint on the generic type to inherit from EntityBase.
That means that the interface should inherit from EntityBase class which is obviously not possible. so his interface should have been some sort of another SpecificEntity class that inherit from EntityBase. Well, that’s no good as some of our existing entities already inherit from the EntityBase and Moran wanted to use them in the collection. That means that Moran should have change the signature of this class (among others):
public class Zone : SpecificEntity //EntityBase
{
// … snippet …
}
The problem was that Zone was inherited also by another class and we couldn’t change that. To make a long story short, our headaches were a result of one simple fact:
One of the biggest drawback of inheriting from a Base Class is [your answer here].
You right, the biggest drawback is that you can do it only *once*.
Using abstract classes with interfaces behind is a very good practice, in my book anyway, but using it(abstract class) as a constraint in a Generic Type can cause you some problems later on. The only thing we did was to change the signature of EntityCollection to:
public class EntityCollection<ENT> : List<ENT>, ISerializable
where ENT : IPersistentEntity
{
// …
}
That made our life easier as we can always implement additional interface(s) in our class.
Epilogue:
The drawback of single inheritance from class is a dangerous pitfall you should avoid while working with Generics constraints, so my tip for you is:
Constraint your generic type(s) by Interface and not by Base Class.