Wrox Home  
Search
Professional .NET 2.0 Generics
by Tod Golding
October 2005, Paperback


Inheritance

Generic concepts, of course, are not constrained to a single class declaration. The type parameters that are supplied with a generic class declaration can also be applied to all the objects that participate in an object hierarchy. Here's a simple example that creates a generic base class and subclasses it with another generic class:

[VB code]
Public Class MyBaseClass(Of U)
    Private _parentData As U

    Public Sub New()
        ... 
    End Sub

    Public Sub New(ByVal val As U)
        Me._parentData = val
    End Sub
End Class

Public Class MySubClass(Of T, U)
    Inherits MyBaseClass(Of U)
    Private _myData As T

    Public Sub New()
        ... 
    End Sub

    Public Sub New(ByVal val1 As T, ByVal val2 As U)
        MyBase.New(val2)
        Me._myData = val1
    End Sub
End Class
[C# code]
public class MyBaseClass<U> {
    private U _parentData;

    public MyBaseClass() {}

    public MyBaseClass(U val) {
        this._parentData = val;
    }
}

public class MySubClass<T, U> : MyBaseClass<U> {
    private T _myData;

    public MySubClass() {}

    public MySubClass(T val1, U val2) : base(val2) {
        this._myData = val1;
    }
}

Notice here that you have MyBaseClass, which accepts a single type parameter. Then, you subclass MyBaseClass with MySubClass, which accepts two type parameters. The key bit of syntax to notice here is that one of the type parameters used in the declaration of MySubClass was also referenced in the declaration of the parent class, MyBaseClass.

Although this example simply passed the type parameters from the subclass to the base class, you can also use type arguments when inheriting from another class or implementing a generic interface. In this case, your generic class declarations could appear as follows:

[VB code]
Public Class MyBaseClass(Of U) 
    ...
End Class

Public Class MySubClass(Of T) 
    Inherits MyBaseClass(Of Integer)
    ...
End Class
[C# code]
public class MyBaseClass<U> {
}

public class MySubClass<T> : MyBaseClass<int> {
}

The subclass has been altered here and now only accepts a single type parameter. And, in place of the type parameter U that was being passed to MyBaseClass, you now pass the type argument Integer. After looking at these two examples, it should be clear that your classes can subclass another class using both open and constructed types (and some variations in between). It's probably obvious at this stage, but I should point out that your generic classes can also subclass non-generic, closed base classes as well.

Now that you know what works, let's take a quick look at some of the combinations of inheritance patterns that will not work. Suppose you have a closed type that inherits from a generic class:

[VB code]
Public Class MyBaseClass(Of T, U)
End Class

Public Class MySubClass1
    Inherits MyBaseClass(Of T, U)
End Class

Public Class MySubClass2
    Inherits MyBaseClass(Of Int32, String)
End Class 
[C# code]
public class MyBaseClass<T, U> { }
   
public class MySubClass1 : MyBaseClass<T, U> { }

public class MySubClass2 : MyBaseClass<int, string> { }

In this example, you have two closed types that inherit from a generic class. MySubClass1 attempts to inherit using an open type and MySubClass2 inherits as a constructed type. When you attempt to compile this code, you're going to notice that MySubClass1 will generate an error. The compiler has no point of reference that allows it to resolve the types of T and U. As a constructed type, MySubClass1 doesn't accept any type parameters and, therefore, has no parameters that can be used because it inherits from MyBaseClass<T, U>. MySubClass2 doesn't accept type parameters either, but it still compiles because it uses type arguments and forms a constructed type as part of its inheritance declaration.

There's one more inheritance scenario that's worth discussing here. Let's look at an example where you use a constructed type as a type argument in the declaration of your inherited class:

[VB code]
Imports System.Collections.Generic

Public Class MyBaseClass(Of T, U)
End Class

Public Class MySubClass1(Of T)
    Inherits MyBaseClass(Of List(Of T), T)
End Class

Public Class MySubClass2(Of T)
    Inherits MyBaseClass(Of List(Of T), Stack(Of T))
End Class
[C# code]
using System.Collections.Generic;

public class MyBaseClass<T, U> { }

public class MySubClass1<T> : MyBaseClass<List<T>, T> { }

public class MySubClass2<T> : MyBaseClass<List<T>, Stack<T>> { }

If you look at this closely, it's really just a variation on one of the earlier examples of inheritance. The key difference here is that a mixture of constructed types and type parameters are used where the constructed types end up leveraging the type parameter from the subclass. The goal here is just to get you comfortable with the possibilities and to get you to view a constructed type like any other type argument you might pass when inheriting from a generic class.

Finally, it's worth noting that a generic class cannot use one of its type parameters as its inherited type. It must descend from an existing closed or open type. For example, the following would not be legal:

[VB code]
Public Class MyType(Of T)
   Inherits Of T
   ...
End Class
[C# code]
public class MyType<T> : T {
    ...
}

At this stage, after looking at all these examples, you should have a much better idea for how the mechanics of generic inheritance work. The rules that govern inheritance are fairly logical and don't really fall outside what you might expect. The main idea to take away here is that you can use both type parameters and type arguments as parameters when subclassing a generic class.