Exploring the impact of Inheritance in Generic Classes

Article translations Article translations
Article ID: 555909 - View products that this article applies to.
Author: Balamurali Balaji MVP
Expand all | Collapse all

SUMMARY

This article explains how to use generic classes in inheritance and explore the possibilities of generic sub-classes, multiple generic types etc.

SYMPTOMS

Exploring the impact of Inheritance in Generic Classes
 
Introduction
 
Generic Classes in C# 2.0 are not new. Most of the programmers have already dwelve into the concept of Generics and created them for use in untyped data structures. They provide a much cleaner way of programming your data without commiting the type of data you handle at the time of compilation. Also, the burden of re-writing the code for different types are completely eliminated allowing you to significantly increase the performance of code.
 
In this article, I would like to focus on the effects of Generics in Inheriting the classes. Inheriting a class from another class leaves you a big question of handling of generic types in both the base class and sub-classes.
 
Inheritance and Generics
 
Creating a generic class with generic types defined in it completes the task of creating a base class. The following example creates Person generic class with a constructor taking two parameters. Note that the generic type is defined as T in the constructor so that during the runtime user would come to know about what type of data he is dealing with. Secondly, the user would have a choice of choosing any type of data working with the Person class. To be precise, the field pid could be of any data type(int, string, short etc.)
 

    public class Person<T>
    {
        public T pid;
        public string pName;
        public Person(T x, string y)
        {
            pid = x;
            pName = y;
        }
    }

The interesting part comes when you inherit this base class. The developer has to decide on two options based on requirements. Do the sub-class is also generic one? Or, is it a regular class that derives the generic base class?
 
Closed Construct generic  is the term used to refer a scenario where a sub-class is non-generic and the base class is parameterized as concrete type.
 

public class SubClass : BaseClass<int>   {...}

Open Construct generic is the term used to refer a scenario where both the base and sub-class is parameterized as generic type.

public class SubClass<T> : BaseClass<T> {...}

 
Let me explain both the ways one by one.
 
Look at the following code:
 
 
    public class Student : Person<int>
    {
        public Student(int x, string y) : base(x,y) { }
        public void Show()
        {
            Console.WriteLine("Student Details: {0}, {1}", this.pid, this.pName);
        }
    }
 
 
The Student class derives from the base class Person and has a default constructor calling the base class constructor and receiving the two parameters(int, string). As the first parameter is of generic type, the Student sub-class clearly identifies the field 'pid' as 'int' datatype. It requires the user to pass an int value during the instantiation of the sub-class Student. So, the Student class is strongly-typed and any number of instances you create for Student, they all have their 'pid's int type.
 
Generics in use
 
The real use of Generics is re-using of the Generic type when you want to create another sub-class. The Person class is derived as Employee class and the pid for Employee object need to be string type. The Employee class is defined as below:
 
 
    public class Employee : Person<string>
    {
        public Employee(string x, string y) : base(x,y) { }
        public void Show()
        {
            Console.WriteLine("Employee Details: {0}, {1}", this.pid, this.pName);
        }
    }
Here again, the constructor of the Employee class calls the base class constructor, passing the two parameters (string, string). Employee class is strongly-typed and any number of instances you create for Employee, they all have their 'pid's int type.
 
In the main method, you call the method in the sub-classes as below:
 
        public static void Main()
        {
            Student student1 = new Student(100, "Bala");
            student1.Show();
            Employee emp1 = new Employee("E100", "Bala");
            emp1.Show();
        }

Notice how a generic class is re-used, safely-typed and easily-inherited with various sub-classes catering to different needs, different types and different purposes.
 
Generic Sub Classes
 
Creating sub-classes of generic type is rarely preferred as you can instantiate objects of same type(class) with different types of data.
 
To be clear, let us take the following sub-class definition as an example.
 

    public class Author<T> : Person<T>
    {
        public Author(T x, string y) : base(x, y) { }
        public void Show()
        {
            Console.WriteLine("Author Details: {0}, {1}", this.pid, this.pName);
        }
    }

The Author class is a generic sub-class derived from Person generic class. Note that both the classes use 'T' as a generic type. The constructor of the sub-class calls the base class constructor passing the user supplied two parameters (T, string). Here, during the run-time, T could be of any data type allowing the field 'pid' of any datatypes.
 
When you want to call a method from two different instances, your code would look like this:
 
       public static void Main()
        {
            Author<int> author1 = new Author<int>(1000, "Bala");
            author1.Show();
            Author<string> author2 = new Author<string>("A1000", "Bala");
            author2.Show();
        }
 
Though it is not quite natural to have multiple types of data on a single fields(you may not store 'pid' as both numeric, alphabetic or alpha-numeric), this type of sub-classes allows you create different types of instances altogether. On looking at the collection of objects, one may find 'pid's of authors taking different type of values.
 
This is because, the generic type is used as a constructor parameter for a primary field, a field of uniquely important to identify an object, the above scenario is not much likened by many programmers. On the other hand, if a generic type of a sub-class is used for non-primary, non-key fields in a class and not included in parameterized constructor, the implementation of generic sub-classes would've been much more clearer.
 
    public class Person<T>
    {
        public int pid;
        public string pName;
        public T pType;
        public Person(int x, string y)
        {
            pid = x;
            pName = y;
        }
    }
 

Given a small change in the definition of Person<T> base class, the sub-class would not have much of a change compared to the ones specified in the previous example.
 

   public class Manager: Person<int>
    {
        public Manager(int x, string y) : base(x,y) { }
        public void Show()
        {
            Console.WriteLine("Manager Details: {0}, {1}, {2}", this.pid, this.pName, this.pType);
        }
    }
At the time of creating objects for the base class, you may use the generic type for the field 'pType' as required.
 
       public static void Main()
        {
            Manager<int> mgr1 = new Manager<int>(1001, "Bala");
            mgr1.pType= 1;
            mgr1.Show();
 
            Manager<string> mgr2 = new Manager<string>(1001, "Bala");
            mgr2.pType = "Admin";
            mgr2.Show();
        }
 
 
Applying constraints on Inherited Generic Classes
 
Constraints in general restrict the types you use to instantiate a generic class. If the class you define as generic also specifies the types it could work-upon, that would give you a more control over the implementation of generic objects. Consider the Person generic class we defined earlier in the article. Now I have done some modification that includes a constraint new(), along with a parameter-less constructor is required. Without bothering the type of 'pid' at the time of compilation, you can still go with this type of generic class especially when you want to initialize the fields, not passing any values.
 
    public class Person<T> where T: new()
    {
        public T pid;
        public string pName;
        public Person()
        {
            pid = new T();
            pName = "";
        }
    }

You can simply instantiate the Person class with default constructor and set the values for the fields later on. But, don't forget that the value of pid is of new T type, in this case it is int.
 
     Person<int> p = new Person<int>();
     p.pid = 1;
     p.pName = "Bala";

But, actual restriction comes on to play when you add the constraints to the sub-class. The class below has a restriction that the generic type must be of Employee type.
 
    public class Supervisor<T> : Person<T> where T : Employee
    {
        public Supervisor(T x, string y) : base(x, y) { }
        public void Show()
        {
            Console.WriteLine("Employee Details: {0}, {1}", this.pid.pid, this.pid.pName);
            Console.WriteLine("Supervisor Details: {0}", this.pName);
        }
    }

 
Note that the first parameter is of Employee Type(in this example, it is pid) and then you would have access to the members of the type as well(pid.pid, pid.pname). To implement this type of constraints in inherited classes, you must first create an object for the Employee class, and then use it as an argument for parameterized constructor to create an instance for the sub class, Supervisor.
 
     Employee emp1 = new Employee("E100", "Bala");
    Supervisor<Employee> super1 = new Supervisor<Employee>(emp1, "Murali");
    super1.Show();

 
Multiple Types and Inheritance
 
How about using multiple types in the base class?
 
    public class Person<T,U>  
    {
        public T pid;
        public U pName;
        public Person(T x, U y)
        {
            pid = x;
            pName = y;
        }
    }

A non-generic sub-class with two generic types inherited from base class would be define like this:
 
    public class Staff : Person<int,string>
    {
        public Staff(int x, string y) : base(x, y) { }
        public void Show()
        {
            Console.WriteLine("Staff Details: {0}, {1}", this.pid, this.pName);
        }
    }

You must include all the generic types defined in the base class. The parameterized constructor for the sub-class is concrete as with the base class also. The sub-class Staff simply inherits the types of base class and hence with out botheration you may create instance for the Staff as below:

       Staff staff1 = new Staff(11, "Bala");
       staff1.Show();

If you want the sub-class also as generic, it must include the types similar to ones specified in the base class, in its constructor.
 
    public class Staff<T,U> : Person<T,U>
    {
        public Staff(T x, U y) : base(x, y) { }
        public void Show()
        {
            Console.WriteLine("Staff Details: {0}, {1}", this.pid, this.pName);
        }
    }
 
 
To call a method on this sub-class object,

            Staff<int, string> staff1 = new Staff<int, string>(11, "Bala");
            staff1.Show();

You can include any number of generic types specific to the sub class only. Look at the following example that shows an additional field 'stafftype' which is of generic type. Now, you get a scenario as the base class has two generic types and the sub-class three generic types; out of three, two are derived from base class.
 

    public class Staff<T,U,V> : Person<T,U>
    {
        V staffType;
        public Staff(T x, U y, V z) : base(x, y) { staffType = z; }
        public void Show()
        {
            Console.WriteLine("Staff Details: {0}, {1}, {2}", this.pid, this.pName, staffType);
        }
    }

When you want to instantiate the sub class, you will be passing values to all the generic types declared in the constructor parameters.

        Staff<int, string, string> staff1 = new Staff<int, string, string>(11, "Bala", "Contract");
    staff1.Show();

 
Conclusion
 
This article does not deal with generic methods and interfaces which would make a greater impact on the way we do inheritance of generic classes. It mostly describes the different scenarios of using derived classes with examples. It is important to understand different ways of defining the generic base class and sub-classes and instantiating the objects appropriately for each case.
 
 
 

Properties

Article ID: 555909 - Last Review: April 3, 2007 - Revision: 1.0
APPLIES TO
  • Microsoft Visual C# .NET 2003 Standard Edition
Keywords: 
kbpubmvp kbpubtypecca kbhowto KB555909
COMMUNITY SOLUTIONS CONTENT DISCLAIMER
MICROSOFT CORPORATION AND/OR ITS RESPECTIVE SUPPLIERS MAKE NO REPRESENTATIONS ABOUT THE SUITABILITY, RELIABILITY, OR ACCURACY OF THE INFORMATION AND RELATED GRAPHICS CONTAINED HEREIN. ALL SUCH INFORMATION AND RELATED GRAPHICS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT AND/OR ITS RESPECTIVE SUPPLIERS HEREBY DISCLAIM ALL WARRANTIES AND CONDITIONS WITH REGARD TO THIS INFORMATION AND RELATED GRAPHICS, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, WORKMANLIKE EFFORT, TITLE AND NON-INFRINGEMENT. YOU SPECIFICALLY AGREE THAT IN NO EVENT SHALL MICROSOFT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, PUNITIVE, INCIDENTAL, SPECIAL, CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF USE, DATA OR PROFITS, ARISING OUT OF OR IN ANY WAY CONNECTED WITH THE USE OF OR INABILITY TO USE THE INFORMATION AND RELATED GRAPHICS CONTAINED HEREIN, WHETHER BASED ON CONTRACT, TORT, NEGLIGENCE, STRICT LIABILITY OR OTHERWISE, EVEN IF MICROSOFT OR ANY OF ITS SUPPLIERS HAS BEEN ADVISED OF THE POSSIBILITY OF DAMAGES.

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com