C# Generics Tutorial

View more categories:

1- Generics Class

The example below defines a  generics class.  KeyValue is a generics class that contains pairs of key and value.
KeyValue.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
   public class KeyValue<K, V>
   {

       private K key;
       private V value;

       public KeyValue(K key, V value)
       {
           this.key = key;
           this.value = value;
       }

       public K GetKey()
       {
           return key;
       }

       public void SetKey(K key)
       {
           this.key = key;
       }

       public V GetValue()
       {
           return value;
       }

       public void SetValue(V value)
       {
           this.value = value;
       }

   }

}
K, V in   KeyValue <K, V> class  is called generics parameters which  is a certain type of data. When using this class, you must determine the specific parameter.
Take the example of using KeyValue class
KeyValueDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    public class KeyValueDemo
    {

        public static void Main(string[] args)
        {
            // Create KeyValue object.
            // int: Phone Number (K = int)
            // string: Name (V = string)
            KeyValue<int, string> entry = new KeyValue<int, string>(12000111, "Tom");

            // C# understands that the return type is a int
            int phone = entry.GetKey();

            // C# understands that the return type is a string
            string name = entry.GetValue();

            Console.WriteLine("Phone = " + phone + " / name = " + name);

            Console.Read();
        }

    }

}
Running Example:

2- Inheritance Generics class

A class extended from a generics class  can specify parameter type for generics, retain generics parameters  or add  generics parameters .

Example 1:

PhoneNameEntry.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{

    // This class extends KeyValue<K,V>
    // And specify the type for the K & V
    // K = int (Phone Number)
    // V = string (Name)
    public class PhoneNameEntry : KeyValue<int, string>
    {

        public PhoneNameEntry(int key, string value)
            : base(key, value)
        {

        }

    }

}
Example use PhoneNameEntry:
PhoneNameEntryDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    public class PhoneNameEntryDemo
    {

        public static void Main(string[] args)
        {

            PhoneNameEntry entry = new PhoneNameEntry(12000111, "Tom");

            // C# understands that the return type is int.
            int phone = entry.GetKey();

            // C# understands that the return type is string.
            string name = entry.GetValue();

            Console.WriteLine("Phone = " + phone + " / name = " + name);

            Console.Read();

        }

    }

}

Example 2:

StringAndValueEntry.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{

    // This class extends KeyValue<K,V>
    // Specify the parameter K is string.
    public class StringAndValueEntry<V> : KeyValue<string, V>
    {

        public StringAndValueEntry(string key, V value)
            : base(key, value)
        {
        }

    }

}
Example use StringAndValueEntry class:
StringAndValueEntryDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    public class StringAndValueEntryDemo
    {

        public static void main(String[] args)
        {

            // (Emp Number, Employee Name)
            // V = string (Employee Name)
            StringAndValueEntry<String> entry = new StringAndValueEntry<String>("E001", "Tom");

            String empNumber = entry.GetKey();

            String empName = entry.GetValue();

            Console.WriteLine("Emp Number = " + empNumber);
            Console.WriteLine("Emp Name = " + empName);

            Console.Read();

        }
    }

}

Example 3:

KeyValueInfo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{

    // This class extends KeyValue<K,V>.
    // It has added a parameter generics I.
    public class KeyValueInfo<K, V, I> : KeyValue<K, V>
    {

        private I info;

        public KeyValueInfo(K key, V value)
            : base(key, value)
        {
        }

        public KeyValueInfo(K key, V value, I info)
            : base(key, value)
        {
            this.info = info;
        }

        public I GetInfo()
        {
            return info;
        }

        public void GetInfo(I info)
        {
            this.info = info;
        }

    }

}

3- Generics Interface

Interface with Generics:
GenericInterface.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
  public interface GenericInterface<G>
  {

      G DoSomething();

  }

}
For example, an implementation of the interface:
GenericInterfaceImpl.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{

   public class GenericInterfaceImpl<G> : GenericInterface<G>
   {

       private G something;

       public G DoSomething()
       {
           return something;
       }

   }

}

4- Using Generics with Exception

You can create an Exception with Generics parameters.
MyException.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
   class MyException<E> : ApplicationException
   {

   }

}
Using the Generic Exception (Valid):
UsingGenericExceptionValid01.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    class UsingGenericExceptionValid01
    {

        public void SomeMethod()
        {
            try
            {
                // ...
            }
            // Valid
            catch (MyException<string> e)
            {
                // Do something here.
            }
            // Valid
            catch (MyException<int> e)
            {
                // Do something here.
            }
            catch (Exception e)
            {
            }
        }

    }

}
Using the Generic Exception (Valid):
UsingGenericExceptionValid02.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    class UsingGenericExceptionValid02<K>
    {
        public void SomeMethod()
        {
            try
            {
                // ...
            }
            // Valid
            catch (MyException<string> e)
            {
                // Do something here.
            }
            // Valid
            catch (MyException<K> e)
            {
                // Do something here.
            }
            catch (Exception e)
            {
            }
        }
    }

}
Using the Generic Exception (Invalid):
UsingGenericExceptionInvalid.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    class UsingGenericExceptionInvalid
    {
        public void SomeMethod()
        {
            try
            {
                // ...
            }
            // Valid
            catch (MyException<string> e)
            {
                // Do something here.
            } 
            // Invalid (Unknown parameter K) *********** 
            // catch (MyException<K> e)
            // {
                // ...            
            // }
            catch (Exception e)
            {
            }
        }
    }

}

5- Generic Methods

A method in class or Interface may be generified. 
MyUtils.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    public class MyUtils
    {
        // <K, V>: To say that this method has two parameters K, V
        // Method returns K.
        public static K GetKey<K, V>(KeyValue<K, V> entry)
        {
            K key = entry.GetKey();
            return key;
        }

        // <K, V>: To say that this method has two parameters K, V
        // Method returns V.
        public static V GetValue<K, V>(KeyValue<K, V> entry)
        {
            V value = entry.GetValue();
            return value;
        }

        // List <E>: The list contains the elements of type E
        // This method returns the type E.    
        public static E GetFirstElement<E>(List<E> list, E defaultValue)
        {
            if (list == null || list.Count == 0)
            {
                return defaultValue;
            }
            E first = list.ElementAt(0);
            return first;
        }

    }

}
For example, using generics method :
MyUtilsDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    public class MyUtilsDemo
    {

        public static void Main(string[] args)
        {

            // K = int: Phone
            // V = string: Name
            KeyValue<int, string> entry1 = new KeyValue<int, String>(12000111, "Tom");
            KeyValue<int, string> entry2 = new KeyValue<int, String>(12000112, "Jerry");

            // (K = int).
            int phone = MyUtils.GetKey(entry1);
            Console.WriteLine("Phone = " + phone);

            // A list containing the element type KeyValue<int, string>.
            List<KeyValue<int, string>> list = new List<KeyValue<int, string>>();

            // Add element to list
            list.Add(entry1);
            list.Add(entry2);

            KeyValue<int, string> firstEntry = MyUtils.GetFirstElement(list, null);

            if (firstEntry != null)
            {
                Console.WriteLine("Value = " + firstEntry.GetValue());
            }

            Console.Read();
        }

    }

}
Running example:

6- Generic Object Initialization

Sometimes, you want to initialize a  Generic object:
public void DoSomething<T>()
{

     // Generic Object Initialization
     T t = new T(); // Error

}
The cause is T parameter is unlikely that it has constructor T(), so you need to add  when T: new() constraints. See the example below:
GenericInitializationExample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
    class GenericInitializationExample 
    {
        // Type T must have default constructor.
        public void DoSomeThing<T>()
            where T : new()
        {
            T t = new T();
        }

        // Type K must have default constructor.
        // and extends KeyValue<K,string>
        public void ToDoSomeThing<K>()
            where K: KeyValue<K,string>, new( )
        {
            K key = new K();
        }


        public T DoDefault<T>()
        {
            // Return null if T is reference type
            // or return 0 if T is number (int, float,..) 
            return default(T);
        }
    }

}

7- Generic Array

In C # you can declare an array of generics:
// Initialize an array.
T[] myArray = new T[10];
GenericArrayExample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GenericsTutorial
{
   class GenericArrayExample
   {

       public static T[] FilledArray<T>(T value, int count)
       {
           T[] ret = new T[count];
           for (int i = 0; i < count; i++)
           {
               ret[i] = value;
           }
           return ret;
       }

       public static void Main(string[] args)
       {

           string value = "Hello";

           string[] filledArray = FilledArray<string>(value, 10);

           foreach (string s in filledArray)
           {
               Console.WriteLine(s);
           }
       }
   }

}

View more categories: