Generics: Base Class Constraint

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Generics represent the new type introduced in .NET Framework 2.0. By understanding generics, you gain the ability to create classes, interfaces, methods, and delegates that can manipulate any kind of data in type safe mode. For sure, you have noticed that many times you had to implement some logical functionality in your application but for different data types. By using generics, this issue is solved. It would be false to stipulate that C# didn’t offer solutions to create generalized classes or methods before. This was possible by using “object” references, but it wasn’t type safe at all and also performing boxing and un-boxing operations was often required. The purpose of “generics” is to solve all these deficiencies. Here is a short example of a generic class:

public class MyGeneric<T>
{
   T obj;

   public MyGeneric(T o)
   {
      obj = o;
   }

   public void ShowGenericType()
   {
      Console.WriteLine("Type of T is: " + typeof(T));
   }
}

This is a very simple piece of code that doesn’t require too much explanation. You can observe that the “MyGeneric” class has a parameter, T, that in the next example will be replaced by a real type. “T” isn’t anything other than a placeholder for the real type. The same thing goes for the constructor that initializes the private object. There is a public method, “ShowGenericType”, that does nothing more than show the real type of the generic class. Here is an example of how the generic class could be used:

class UsageOfGeneric
{
   public static void Main()
   {
      MyGeneric<int> objInt = new MyGeneric<int>(12);
      objInt.ShowGenericType();

      MyGeneric<string> objString =
         new MyGeneric<string>("I'm generic");
      objString.ShowGenericType();
   }
}

In the preceding code, you can see that you have created two generic objects of different types. The first is an object of type integer (MyGeneric<int> objInt = new MyGeneric<int>(12);), to which constructor you have passed an integer value; the second one is an object of type string (MyGeneric<string> objString = new MyGeneric<string>(“I’m generic”);), to which constructor you have passed a string value. The result of the ShowGenericType() method is clear that, for the object of type integer, the output is System.Int32; for the object of type string, the output is System.String. Event in the previous example, I had used only one generic parameter “T”, you are not limited to that. You can use as many generic parameters for your class as you want. You just have to place a comma between them (MyGeneric<T,V>). One major observation is that a reference of one version of a generic type is not compatible with a different version of the same generic type (objInt != objString).

In the previous cases, type T could be anything because you didn’t care. But, sometime you might want to impose a restriction for the generic types. The reason is quite straightforward; just think that in the generic class “MyGeneric” you have a method that needs to invoke a specific member from type T. To solve this issue, you have to take into consideration the fact that generics support constraints. There are five types of constraints supported by generics:

  1. Base class constraint: You can require a certain base class to be present in the type argument.
  2. Interface constraint: You can require one or multiple interfaces to be implemented by the type argument.
  3. Constructor constraint: You can require the type argument to supply a constructor without parameters; for example, new().
  4. Reference type constraint: You can require that the type argument be a reference type.
  5. Value type constraint: You can require that the type argument to be a value type; for example, struct.

Base Class Constraint

Base class constraint allows you to specify a base class from which the type argument must inherit. The main reasons for this type of constraint are: first, it allows you to use members of the based class; second, the base class constraint ensures that only arguments of base class type can be used in the generic class. For the second reason you have two options: the argument type can be either the base class itself or it can be any class derived from the specified base class. Here is an example of a base class constraint:

public class B : A
{

}

public class C
{

}

public class MyGeneric<T> where T : A
{
   private T objA;

   public MyGeneric(T obj)
   {
      objA = obj;
   }

   public void MyHello()
   {
      objA.HelloWorld();
   }
}

class UsageOfGenerics()
{
   public static void Main()
   {
      A a = new A();
      B b = new B();
      C c = new C();

      // Correct line of code.
      // Class A is the base class.
      MyGeneric<A> t1 = new MyGeneric(a);

      // Correct line of code.
      // Class B inherits from base class A.
      MyGeneric<B> t2 = new MyGeneric(b);

      // Incorrect line of code.
      // Class C doesn't inherit from base class A.
      MyGeneric<C> t3 = new MyGeneric(c);
   }
}

So, again the main reasons why base class constraint should be used are these: It enables the generic class to access members of the base class, and it ensures that only arguments types of base class type are valid obtaining a type safe environment.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read