o7planning

C# Generics Tutorial with Examples

  1. Generics Class
  2. Inheritance Generics class
  3. Generics Interface
  4. Using Generics with Exception
  5. Generic Methods
  6. Generic Object Initialization
  7. Generic Array

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:
Phone = 12000111 / name = Tom

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:
Phone = 12000111
Value = Tom

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);
           }
       }
   }
}