Vytvoření instance generika v run-time
Vytvořit instanci generické třídy v runtime není sice žádná
alchymie, ale není to ani záležitost přímočará a intuitivní.
Klíčová je metoda Type.MakeGenericType().Ta
má jako parametry (params) pole
typů, které odpovídají typovým parametrům generika, a vrací typ, který je možno
pomocí třídy Activator
instanciovat jako generický. Nejlépe bude vše vidět na příkladu; jako cvičný
typ v ukázkách poslouží System.Collections.Generic.List<T>:
Typ, na kterém bude volána metoda MakeGenericType() lze získat například takto:
private void V3()
{
Type
baseListType = typeof(List<>);
Type
intListType = baseListType.MakeGenericType(typeof(int));
object
o = Activator.CreateInstance(intListType);
}
Upozorňuji na syntaxi zápisu generika pro typeof, která je List<>, tedy
bez uvedení konkrétního typu.
Příslušný typ lze samozřejmě získat i s pomocí jména
zadaného jako řetěz, což bude v run-time zřejmě častější případ:
private void V4()
{
Type
baseListType = Type.GetType("System.Collections.Generic.List`1");
Type
intListType = baseListType.MakeGenericType(typeof(int));
object
o = Activator.CreateInstance(intListType);
}
Opět upozorňuji na syntaxi zápisu generika, která se liší od
syntaxe C#, a vypadá následovně: List`1. Je to mimochodem stejná
forma, kterou používá IL kód.
Poznámky:
Errata:
1.) Je-li generický typ
parametrizován dvěma a více typy, nelze jej získat pomocí typeof, ale pouze přes jeho jméno ve formátu Type`n, kde n je počet parametrů. Příklad:
1.) Je-li generický typ
parametrizován dvěma a více typy:
class C1<A, B> {}
lze je získat následujícím způsobem:
private void V5a()
{
Type
baseListType = typeof(C1<,>);
Type
listType = baseListType.MakeGenericType(typeof(int), typeof(string));
object o
= Activator.CreateInstance(listType);
}
nebo přes jeho jméno ve formátu Type`n, kde n je počet parametrů:
private void V5b()
{
Type
baseListType = Type.GetType("Namespace.C1`2");
Type
listType = baseListType.MakeGenericType(typeof(int), typeof(string));
object
o = Activator.CreateInstance(listType);
}
2.) Je-li typ již
parametrizován, další volání MakeGenericType() způsobí InvalidOperationException, jako v následujícím příkladě:
private void V3a()
{
Type
intListType = typeof(List<int>);
Type
stringListType = intListType.MakeGenericType(typeof(string));
List<string> intList = (List<string>)Activator.CreateInstance(stringListType);
intList.Add("1");
}