C# Generics Tutorial
View more Tutorials:

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:

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

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