Why Generics?
Generics provide the solution to a limitation in earlier versions of the common language runtime and the C# language in which generalization is accomplished by casting types to and from the universal base type Object. By creating a generic class, you can create a collection that is type-safe at compile-time.
By allowing you to specify the specific types acted on by a generic class or method, the generics feature shifts the burden of type safety from developer to the compiler. There is no need to write code to test for the correct data type, because it is enforced at compile time. The need for type casting and the possibility of run-time errors are reduced.


Also Generics got rid of disadvantage of array list by avoiding the type casting.
Let us go through the simple example with usage of Generics.

Implementation of Generics
The first step in generics is to create the list of generic template for which the class is applicable.

Let us take an example of creating Generic collection using the simple bank requirement below. Requirement is to create the collection of either Current Bank account or Savings bank account.

Create two classes.
Savings bank account
Current Bank account

SavingsBank.cs

using System;
using System.Collections.Generic;

using System.Text;
namespace GenericImplementation
{
    class SavingsBank
    {
        protected string name;
        protected int age;
        private const float MIN_AMT_LMT = 10000;

        private float  accBalance;
        public SavingsBank(string AccName, int age, string opType, float balanceAmt)
        {
            this.Name = AccName;
            this.Age = age;
            if (opType.ToLower().Equals("credit"))
            {
               this.DoCredit(balanceAmt);
            }
            else
            {
                this.DoDebit(balanceAmt);
            }
        }
        public string Name
        {
         get
          {
            return name;
          }
            set
            { name = value;
            }
        }
       public int Age
        {
            get
            {
            return age;
            }
            set
            { age = value;
            }
        }
        public float TotalBalance
        {
            get
            {
                return accBalance;
            }
            set
            {
                accBalance = value;
            }
        }
        public void DoCredit(float credBal)
        {
            TotalBalance = TotalBalance + credBal;
        }
        public void DoDebit(float debBal)
        {
            if (TotalBalance - debBal > MIN_AMT_LMT)
            {
            TotalBalance = TotalBalance - debBal;
            }
            else
            {
                throw new Exception("Balance should not be less than minimum amount " + MIN_AMT_LMT.ToString());
            }
        }
        public override string ToString()
        {
            return " Account name :" + this.Name + " Age:" + this.age.ToString() + " Balance : " + this.TotalBalance.ToString();
        }
    }
}

Current Bank A/C:
using System;
using System.Collections.Generic;
using System.Text;

namespace GenericImplementation
{
    class CurrentBank
    {
        protected string name;
        protected int age;
        private float accBalance;
        private const float MIN_AMT_LMT = 20000;
        public CurrentBank(string AccName, int age,string opType,float balanceAmt)
        {
            this.Name = AccName;
            this.Age = age;
            if (opType.ToLower().Equals("credit"))
            {
                this.DoCredit(balanceAmt);
            }
            else
            {
                this.DoDebit(balanceAmt);
            }
        }
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public float GetCreditLimit
        {
               get
            {
                return MIN_AMT_LMT;
            }
        }

        public int Age
        {
           get
            {
                return age;
            }
            set
            {
               age = value;
            }
        }
        public float TotalBalance
        {
            get
            {
                return accBalance;
            }
            set
            {
                accBalance = value;
            }
        }
        public void DoCredit(float credBal)
        {
            TotalBalance = TotalBalance + credBal;
        }
        public void DoDebit(float debBal)
        {
            if (TotalBalance - debBal > MIN_AMT_LMT)
            {
                TotalBalance = TotalBalance - debBal;
            }
            else
            {
                throw new Exception("Balance should not be less than minimum amount " + MIN_AMT_LMT.ToString());
            }
       }
        public override string ToString()
        {
            return " Account name :" +this.Name + " Age:" +this.age.ToString() + " Balance : " + this.TotalBalance.ToString();
        }
    }
}
 
Now create a Generic bank account collection which can hold the collection of either Savings Bank account or Current bank account.
Generic Bank Collection:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace GenericImplementation
{
    class GenericBankCollection<AccountType>  : CollectionBase
    {
        public void Add(AccountType GenericObject)
        {
            InnerList.Add(GenericObject);
        }
        public void Remove(int index)
        {
            InnerList.RemoveAt(index);
        }
        public AccountType Item(int index)
        {
            return (AccountType)InnerList[index];
        }
    }
}
 
Usage:
Now we can use the Generic bank collection to hold either the savingsBankAccount or CurrentBankAccount collection.

Program.cs:
using System;
using System.Collections.Generic;
using System.Text;

namespace GenericImplementation
{
    class Program
    {
        static void Main(string[] args)
        {
           GenericBankCollection<SavingsBank> sbAccs = new GenericBankCollection<SavingsBank>();
            sbAccs.Add(new SavingsBank("Peter",34,"credit",1000));
            sbAccs.Add(new SavingsBank("Tom",30,"debit",1000));
            GenericBankCollection<CurrentBank> curAccs = new GenericBankCollection<CurrentBank>();
            curAccs.Add(new CurrentBank("Michael", 34, "credit", 1000));
            curAccs.Add(new CurrentBank("Mark", 30, "credit", 1000));

            System.Console.WriteLine("Savings Accounts");
            System.Console.WriteLine("=========");
            foreach (SavingsBank savingsBank in sbAccs)
            {
                System.Console.WriteLine(savingsBank.ToString());
            }

            System.Console.WriteLine("Current Accounts");
            System.Console.WriteLine("=========");
            foreach (CurrentBank CurrentBank in curAccs)
            {
                System.Console.WriteLine(CurrentBank.ToString());
            }
            System.Console.ReadLine();
    }

    }

}