GENERIC INHERITANCE RULES
There are additional rules you need to follow when inheriting Generic type classes in C#. Remember, your base and derived classes can use different generic types or share them. The following tests have helped me understand the rules. Generic classes can inherit from concrete, closed constructed, or open constructed base classes as follows:
// First create some Base Classes to inherit from.
// Non-generic and generic classes are included.
// Below, I will inherit these in Derived Classes.
// If my inheritance test works, you will see "YES", otherwise "NO".
class BaseType1 { }
class BaseType2<T> { }
class BaseType3<T1,T2> { }
// -------------------------------------------------
// Concrete type inheritance. Here the inheriting type
// does NOT have to be generic as the child
class Derived1 : BaseType1 { }// YES!
class Derived2<T> : BaseType1 { }// YES!
// Open constructed type generic inheritance allows shared
// generic type inheritance.
class Derived3<T> : BaseType2<T> { }// YES!
// Closed constructed type inheritance. Note that what the child
// class accepts as far as types is different from what the
// parent accepts.
// This is allowed as each implements its own types and
// constraints on its members and the parent has defined its type.
class Derived4<T> : BaseType2<int> { }// YES!
// NO! Base type must know its accepted type if the child
// class is not accepting a generic "T" type as well.
class Derived5 : BaseType2<T> { }// NO!
// Another form that limits generic type. Note, here the first
// base type uses a reference type "string" that is different from
// what the derived child type uses, which is any "struct" type or
// int, etc. Because the base type is defined as "string" the
// child can define anything for its generic or not even use a generic.
// The "where" clause only affects the derived class type!
class Derived7<T> : BaseType2<string> where T : struct { }// YES!
class Derived6<T> : BaseType2<T> where T : struct { }// YES!
// NO! The "where" clause cannot be used to define the base class's type!
class Derived8 : BaseType2<T> where T : struct { }// NO!
// NO! "T1" and "T2"must be a concrete type again if the derived
// type doesn't use the same
class Derived9 : BaseType3<T1,T2>// NO!
// Here, both parent and child classes must accept one generic
// type for this to work.
class Derived10<T> : BaseType3<T,T> { }// YES!
// Each generic type must have unique names in the derived class
// AND match the base class names. That is why these fail.
class Derived11<T, T> : BaseType3<T1, T2> { }// NO!
class Derived12<name1, name2> : BaseType3<T1, T2> { }// NO!
class Derived13<T1, T2> : BaseType3<T1, T2> { }// YES!
// As long as one generic name matches between child and parent,
// this shared generic type on child and parent ok.
class Derived14<Tname,T2> : BaseType2<Tname> { }// YES!
// Again, the child class doesn't have to be generic if the base
// generic class has defined the types it accepts.
class Derived15 : BaseType3<string, int> { }// YES!
class Derived16<T1, T2> : BaseType3<string, int> { }// YES!
// Here, you can have the child class accept two generic types,
// while the parent uses its own explicit type.
class Derived17<T1, T2> : BaseType2<string> { }// YES!
// No, you need to define these using "where".
class Derived18<string, int> : BaseType1 { }// NO!
class Derived19<T1, T2> : BaseType1 where T1 : class where T2 : struct { }// YES!
// This generates an error. You cannot inherit a generic type.
class Derived20 : T {}// NO!