Although I use Generics extensively, every once in a while it still throws me for a loop when dealing with complex generic parameters and inheritance. I recently ran into a situation where I needed to created a class hierarchy and needed to inherit generic parameters. Generics are great and for this particular implementation the simple base->specific type inheritance makes it really easy to define the business object classes without each instance having to specify the generic template types.
For something specific consider that I have class that is defined like this:
public abstract class BusinessObjectLinq<TEntity,TContext> : IDisposable
where TEntity: class, new()
where TContext: DataContextSql, new()
{ }
In order to use this class I have to create a subclass that provides specific type instances for TEntity and TContext (which in this case is a Linq to SQL data context type):
public class busEntry : BusinessObjectLinq<EntryEntity, TimeTrakkerContext>
{ }
which can then be instantiated easily enough without providing any generic information:
//protected busEntry Entry = TimeTrakkerFactory.GetEntry();
protected busEntry Entry = new busEntry();
And this works just as you would expect. Because the busEntry type explicitly points at the data model and entity type those generic parameters don’t have to be passed each time an instance of the entry entity is created.
So far so good.
Inheritance in the Middle
This works great for single inheritance – each business object inherits from the base class and as part of that implementation provides the generic parameters. Nothing tricky there. But what if you need to add a type in the middle – say a common application global base business object that provides some common functionality specific to the application.
At first blush this looks simple (and it actually is) but I had a number of false starts trying to make this work. My first (failing) cut at this tried to the obvious and looked like this:
public class BusinessObjectLinq<TEntity, TContext> : IDisposable
where TEntity : class, new()
where TContext : DataContextSql, new()
{
... core bus object operations
}
public class TimeTrakkerBusinessBase<TEntity, TContext> :
BusinessObjectLinq<TEntity, TContext>
{
... application global specific bus operations (few)
}
public class busEntry : TimeTrakkerBusinessBase<EntryEntity, TimeTrakkerContext>
{
... specific implementation for logical business entities
}
but alas, this fails to compile with:
‘TContext' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TContext' in the generic type or method 'Westwind.BusinessFramework.LinqToSql.BusinessObjectLinq<TEntity,TContext>
When this error first hit I immediately assumed that you simply cannot inherit from a generic type and pass generic parameters to the base definition. Specifically I misinterpreted the ‘abstract type’ in the message to mean the non-specific type reference (ie. TEntity). In a way this makes sense – generics expects a concrete type to create the custom class based on these types. I asked around in regards to this and got a lot of the same head-scratching I was going through.
However, if I had read the error message more carefully and given a little more thought to this the error, I would have seen that the error message actually complains about a very specific issue – namely that the generic parameters aren’t provided properly – there’s some ambiguity in that the base type requires only specific object instances. By not restraining the inherited parameters there’s potential that the values provided are not of the same typed as a result – Bonkage!
The solution is to add the same constraints of the base generic class to the inherited application level generic class. So the following does work as expected:
public class TimeTrakkerBusinessBase<TEntity, TContext> :
BusinessObjectLinq<TEntity, TContext>
where TEntity : class, new()
where TContext : DataContextSql, new()
{
}
The key is that the constraints have to be duplicated on the generic subclass in order for the code to compile. After that the behavior works and you can pass the generic parameters from the new class to the base class generic parameter list. Ugh, not obvious but clear like apple pudding in hind sight.
One thing that bites about this is that you have to know what the constraints are. If you own the code then this isn’t much of a problem. However, if you don’t then it’s not so easy to discover what the restraints are exactly.
I use Generics on a regular basis in types I create but to this day getting generic definitions just right can often throw me for a loop and this was one of them. In fact I’ve run into this issue on a few occasions and have actually just given up and implemented this functionality using a different, more composite approach (which was actually a good thing in those cases!). However, in this situation the inheritance actually makes sense (to me at least) and I can’t really see how to implement this behavior via a different composition approach.
Anyway, I hope some of you may find this useful especially if you end up like me running into the compiler error that seemingly forbids generic inheritance.
Other Posts you might also like