Problém bez generiky

Následující příklad ukazuje vytvoření vlastní kolekce, která se nazývá IntList - jedná se o dynamické pole, do kterého je možné ukládat prvky datového typu int. Tato kolekce bude podporovat metody Add(), Get().

Po čase vznikne potřeba vytvořit kolekci pro ukládání řetězců - StringList, která by měla fungovat úplně stejně jako IntList, ale pro řetězce. Provede se tedy zkopírování, vložení a potřebná úprava. Jejich implementace lze vidět níže:

public class IntList
{
	private int _capacity = 4;
	private int _count = 0;
    private int[] _items = new int[4];
 
    public void Add(int item)
    {
	    if (_count >= _capacity)
	    {
            _capacity = _capacity * 2;
		    int[] newItems = new int[_capacity];
		    for(int i = 0; i < _count; i++)
		    {
			    newItems[i] = _items[i];
		    }
		    _items = newItems;
		}
		
        _items[_count] = item;
        _count++;
    }
 
    public int Get(int index)
    {
		if (index < 0 || index >= _count)
	        throw new ArgumentOutOfRangeException(nameof(index));
       
        return _items[index];
    }
}
 
public class StringList
{
    private int _capacity = 4;
    private int _count = 0;
    private string[] _items = new string[4];
 
    public void Add(string item)
    {
        if (_count >= _capacity)
        {
            _capacity *= 2;
            string[] newItems = new string[_capacity];
            for (int i = 0; i < _count; i++)
            {
                newItems[i] = _items[i];
            }
            _items = newItems;
        }
 
        _items[_count] = item;
        _count++;
    }
 
    public string Get(int index)
    {
        if (index < 0 || index >= _count)
            throw new ArgumentOutOfRangeException(nameof(index));
       
        return _items[index];
    }
}

Již na první pohled si lze všimnout, že kód je úplně stejný až na datové typy. Aby nebylo nutné kód duplikovat, je možné vytvořit generický datový typ, kterému se při jeho inicializaci nastaví parametr, s kterým tento typ pracuje. Často se tento parametr prefixuje s T, např. tedy T, TKey, TEntity, atd. Generický parametr se umisťuje vždy do špičatých závorek. V jedné třídě jich může být více, pak se oddělují čárkou. Příklad níže ilustruje vytvoření generické třídy MyList a její použití.

public class MyList<T>
{
    private int _capacity = 4;
    private int _count = 0;
    private T[] _items;
 
    public MyList()
    {
        _items = new T[_capacity];
    }
 
    public void Add(T item)
    {
        if (_count >= _capacity)
        {
            _capacity *= 2;
            T[] newItems = new T[_capacity];
            for (int i = 0; i < _count; i++)
            {
                newItems[i] = _items[i];
            }
            _items = newItems;
        }
 
        _items[_count] = item;
        _count++;
    }
 
    public T Get(int index)
    {
        if (index < 0 || index >= _count)
            throw new ArgumentOutOfRangeException(nameof(index));
       
        return _items[index];
    }
}

Ukázka použití:

var intList = new MyList<int>();
intList.Add(1);
intList.Add(2);
intList.Add(3);
Console.WriteLine(intList.Get(1)); // 2
 
var stringList = new MyList<string>();
stringList.Add("Hello");
stringList.Add("World");
Console.WriteLine(stringList.Get(0)); // Hello

Specifikace generiky

V některých případech je potřebné více specifikovat, jaké typy je možné využít. Od toho slouží klíčové slovo where.

public abstract class Entity
{
    public int Id { get; set; }
}
 
public class Repository<T> where T : Entity
{
    private List<T> _items = new List<T>();
 
    public void Add(T item)
    {
        _items.Add(item);
    }
 
    public T GetById(int id) // Kompilátor ví, že T je entita, která má Id. Je možné tedy filtrovat.
    {
        return _items.FirstOrDefault(item => item.Id == id);
    }
}

Generické metody

Kromě generických tříd, kde lze typový parametr využít kdekoliv v rámci třídy, je možné vytvořit pouze generickou metodu, kde lze generický parametr využít pouze v její hlavičce a jejím těle. Tato metoda může být umístěna i v negenerické třídě.

public static class Helper
{
    public static void Print<T>(T value)
    {
        Console.WriteLine(value);
    }
}