C# Generics

GenericsAllows you to defer the specification of the data type of programming elements in a class or method until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.

You can write specifications for classes or methods by using substitute parameters for data types. When the compiler encounters a class constructor or a method call, it generates code to handle the specified data type. The following simple example will help you understand this concept:

Example

using System;
using System.Collections.Generic;

namespace GenericApplication
{
    public class MyGenericArray<T>
    {
        private T[] array;
        public MyGenericArray(int size)
        {
            array = new T[size + 1];
        }
        public T getItem(int index)
        {
            return array[index];
        }
        public void setItem(int index, T value)
        {
            array[index] = value;
        }
    }
           
    class Tester
    {
        static void Main(string[] args)
        {
            // Declare an integer array
            MyGenericArray<int> intArray = new MyGenericArray<int>(5);
            // Set value
            for (int c = 0; c < 5; c++)
            {
                intArray.setItem(c, c*5);
            }
            // Get value
            for (int c = 0; c < 5; c++)
            {
                Console.Write(intArray.getItem(c) + " ");
            }
            Console.WriteLine();
            // Declare a character array
            MyGenericArray<char> charArray = new MyGenericArray<char>(5);
            // Set value
            for (int c = 0; c < 5; c++)
            {
                charArray.setItem(c, (char)(c+97));
            }
            // Get value
            for (int c = 0; c < 5; c++)
            {
                Console.Write(charArray.getItem(c) + " ");
            }
            Console.WriteLine();
            Console.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following results:

0 5 10 15 20
a b c d e

Features of Generics

Using generics is a technique for enhancing program functionality, specifically in the following aspects:

  • It helps you maximize code reuse, protect type safety, and improve performance.
  • You can create generic collection classes. The .NET Framework class library in theSystem.Collections.Genericnamespace contains some new generic collection classes. You can use these generic collection classes to replaceSystem.CollectionsCollection classes in.
  • You can create your own generic interfaces, generic classes, generic methods, generic events, and generic delegates.
  • You can constrain generic classes to access methods of specific data types.
  • Information about the types used in generic data types can be obtained at runtime through the use of reflection.

Generic Methods

In the above example, we have used generic classes. We can declare generic methods through type parameters. The following program illustrates this concept:

Example

using System;
using System.Collections.Generic;

namespace GenericMethodAppl
{
    class Program
    {
        static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }
        static void Main(string[] args)
        {
            int a, b;
            char c, d;
            a = 10;
            b = 20;
            c = 'I';
            d = 'V';

            // Display values before swapping
            Console.WriteLine("Int values before calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values before calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);

            // call swap
            Swap<int>(ref a, ref b);
            Swap<char>(ref c, ref d);

            // Display values after swapping
            Console.WriteLine("Int values after calling swap:");
            Console.WriteLine("a = {0}, b = {1}", a, b);
            Console.WriteLine("Char values after calling swap:");
            Console.WriteLine("c = {0}, d = {1}", c, d);
            Console.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following results:

Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I

Generic Delegates

You can define generic delegates using type parameters. For example:

delegate T NumberChanger<T>(T n);

The following example demonstrates the use of delegates:

Example

using System;
using System.Collections.Generic;

delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }

        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }

        static void Main(string[] args)
        {
            // Create a delegate instance
            NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
            NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
            // Use delegate object to call method
            nc1(25);
            Console.WriteLine("Value of Num: {0}", getNum());
            nc2(5);
            Console.WriteLine("Value of Num: {0}", getNum());
            Console.ReadKey();
        }
    }
}

When the above code is compiled and executed, it produces the following results:

Value of Num: 35
Value of Num: 175
other extensions