AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1100&pId=-1
Using Generics in C#
page
by Joydip Kanjilal
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 37813/ 59

Introduction

C# is a strongly typed language, i.e., objects in C# should have a type prior to holding any value. This enforces language interoperability and security but restricts the programmer to specify the type of an object at the time of writing the program as the type of an object in C# cannot be determined at runtime. Generics are a new concept that has been introduced with C# 2.0 and it helps us to defer the binding of a generic type to a data type until its point of usage arrives. It is one of the most powerful and anticipated feature of the C# 2.0 language. The basic idea behind generics is to develop universal classes and methods that can accept a type as a parameter to promote reusability, efficiency and maintainability of code. This article discusses Generics, its usage, merits and demerits with code examples wherever necessary.

What are Generics?

According to MSDN, "Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code." By using Generics, classes and methods can work uniformly on the values of different types. Generics facilitate type safety, improved performance and reduced code. It promotes the usage of parameterized types on our types and is also known as parametric polymorphism. The Common Language Runtime (CLR) compiles any Generic type to IL and Metadata as it does with the other types; but it stores added information pertaining to the generic types which is used to bind the generic type to a specific type at runtime when the generic type is instantiated. Note that for generic types that are bound to value types, the generic types are instantiated for each value type that it is bound to. Unlike this, for generic types that are bond to reference types, the generic type instance refers to the location in memory of the reference type to which it is bound for all the instances of the generic type. The following code snippet in Listing 1 illustrates how a generic type can be implemented.

Listing 1

public class Test<T> 
{
    public void Display() 
      {
          //Some code
      }
//Other members
}

We may say that the type parameter is an unbound type as it is not bound to any specific type. When we instantiate this class, the generic type has to be bound to a particular type as shown in the code snippet in Listing 2.

Listing 2

Test<int> test = new Test<int>();

Note that in the code snippet above, the resulting type called "test" is a bound type. The code example in Listing 3 shows how we can use a generic method on varied types.

Listing 3: Implementing a generic method

using System;  
using System.Collections.Generic;  
 
   class Test  
   {  
      static void Main( string[] args )  
      {  
        Test t = new Test();
        int[] integerArray = {1,2,3,4,5,6};  
        char[] characterArray = { 'J', 'O', 'Y', 'D', 'I','P' };   
        double[] doubleArray = {0.1,0.2,0.3,0.4,0.5,0.6};  
         Console.WriteLine( "Displaying the contents of the integer array:--" );  
         t.Display(integerArray);
         Console.WriteLine( "Displaying the contents of the character array:--" );
         t.Display(characterArray);
         Console.WriteLine( "Displaying the contents of the double array:--" );
         t.Display(doubleArray);
      }   
     
      public void Display< GenericArray >( GenericArray[] array )  
      {  
         for (int i = 0; i< array.Length; i++)  
            Console.WriteLine(array[i]);  
      }
   }

The following points sum up the basic advantages of using Generics.

·         Code Efficiency

·         Enhanced performance

·         Type Safety and reliability

·         Maintainability of code

How does C# Generics and C++ Templates compare?

According to MSDN, "Generics and templates are both language features that provide support for parameterized types. However, they are different and have different uses." C# generics and templates in C++ are more or less similar syntactically. However, there are some notable differences between them. C# Generic types are strong typed and they are instantiated at the runtime whereas C++ Templates are loosely typed and they are instantiated at the compile time only. Further, unlike C++ templates, Generics do not permit the type parameters to have default values.

MSDN states, "C++ templates use a compile-time model. When a template is used in a C++ program, the effect is as if a sophisticated macro processor had been used. C# generics are not just a feature of the compiler, but also a feature of the runtime. A generic type such as List<T> maintains its generic-ness (genericity) after it has been compiled. Or, to look at it another way, the substitution that the C++ compiler does at compile time is done at JIT time in the C# generic world."

The System.Collections.Generic Namespace

The System.Collections.Generic namespace contains several generic collection classes based on generics and it is recommended that we should use these collection classes in lieu of the earlier non-generic ones for better performance of our applications.

According to MSDN, "The System.Collections.Generic namespace contains interfaces and classes that define generic collections, which allow users to create strongly typed collections that provide better type safety and performance than non-generic strongly typed collections."

Note that the System.Collections.Generic.ICollection<T> interface is the base interface for all the classes in the System.Collections.Generic namespace.

Implementing a Generic Stack Class

Let us understand the Generics with a simple example. Here I have implemented a stack in two different ways. A stack that can be recollected, works in the LIFO (Last-In-First-Out) principle. If we implement a stack that can accept any data type or object and store the same in to it, we have to opt for an object based stack (obviously if we are not using Generics). I have given here two code examples of a partial implementation of a stack.

The first one is an object based stack and the next a generic stack. The object based stack would require boxing and unboxing operations to store and return objects in and out of the stack, but the generic stack (the one that uses C# Generics) would not. Hence, as far as performance is concerned, the generic stack is the better choice.

The code in Listing 4 is an example of an object based stack. Note that we can store any type of data in this stack, but we need to perform an explicit cast in order to retrieve the right type of the object that was stored using boxing and unboxing. This can be a significant performance overhead.

Listing 4

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Generics
{
    public class CustomStack
    {
        const int size = 10;
        private object[] register;
 
        private int count = 0;
 
        private CustomStack()
        {
            register = new object[size];
        }
        public void Push(object x)
        {
            if (count < size)
                register[count++] = x;
        }
        public object Pop()
        {
            return register[--count];
        }
        static void Main(string[] args)
        {
            CustomStack intStack = new CustomStack();
            intStack.Push(10);
            int i = (int)intStack.Pop();
            Console.WriteLine(i);
            Console.Read();
        }
    }
}

When using the object based stack shown in Listing 1, you have to box and unbox the elements in order to push and pop them in and out of the stack resulting in a significant performance drawback. Now, Generics comes to the rescue. It is much more flexible and removes the overhead involved in boxing and unboxing operations that we discussed earlier as a generic type is assigned a specific type only at runtime. The type checks are done at the compile time itself. Hence, the generic stack in our example works much faster compared to its non-generic counterpart. The following is the implementation of a generic stack using C# Generics.

Listing 5

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Generics
{
    public class CustomStack<S>
    {
        const int size = 10;
        private S[] register;
        private int count = 0;
 
        public CustomStack()
        {
            register = new S[size];
        }
 
        public void Push(S x)
        {
            if (count < size)
                register[count++] = x;
        }
 
        public S Pop()
        {
            return register[--count];
        }     
   }
 
    public class Test
    {
        static void Main(string[] args)
        {
            CustomStack<int> intStack = new CustomStack<int>();
            intStack.Push(10);
            int i = intStack.Pop();
            Console.WriteLine(i);
            Console.Read();
        }
    }
}
References
Conclusion

C# is a type safe language. This implies that an object should have its type defined prior to an assignment of any value to it. Generics, also known as parameterized types or parametric polymorphism, allow us to have type parameters on our types. These generic types are assigned to a specific type at the time of instantiation of the type at runtime. This deferred instantiation of a generic type and its late binding to a specific type promotes its usage on any type. This article has discussed Generics with code illustrations wherever necessary. Happy reading!



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-16 5:04:46 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search