Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Generic Types and Inheritance


:P
On this page:

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.

Posted in .NET  CSharp  

The Voices of Reason


 

Jimmy Bogard
August 18, 2009

# re: Generic Types and Inheritance

Generic constraints are kinda like checked exceptions in java - once you put them in one place, they spread to everything that needs to touch them.

alberto
August 18, 2009

# re: Generic Types and Inheritance

Well, it makes a lot of sense if you think the type of the inherited class is also a type of the base class, so you cannot loose the restrictions you impose on the base class (e.g. must have a parameterless constructor).

Alex Sarafian
August 19, 2009

# re: Generic Types and Inheritance

I have implemented a framework that is based on generics. If you put everything in place they are great.

One serious problem is that offen exceptions that are thrown in the generic class identify the type as something like typename~2 which makes it especially hard to pinpoint the problem.

The constraints generics have for my have one minor problem. The do not take address enough the constraints for value typed objects like int, double etc. The struct constraint is simply not enough.

On a personal note, generics is one of the greatest things that came with .Net 2.0 and was on of the strongest point (as templated) in C++

Great job though.

Jason Haley
August 19, 2009

# Interesting Finds: August 19, 2009

Interesting Finds: August 19, 2009

Yoav
August 19, 2009

# re: Generic Types and Inheritance

Thank you Rick, i know someday this information will come in handy!

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024