Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Ars programatica

Udělat dobrý sotware je řemeslo, udělat výjimečný software je umění
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");
}

Posted: Sunday, March 23, 2008 1:21 PM by pbouda
Vedeno pod: ,

Komentář

Tom napsal:

"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..."

Tak s tim bych lehce nesouhlasil :-), protoze pomoci typeof muzete ziskat otevreny genericky typ takto:

Type openedType = typeof(Namespace.C1<,>);

kde pocet "," = poctu generickych argumentu - 1

# March 24, 2008 11:34 AM

pbouda napsal:

To jsem samozřejmě zkoušel také, ale nefungovalo to. Divil jsem se, ale bral jsem to tak, že tam "nechal tesař díru." Tak jsem to po vašem upozornění zkusil znova a - máte pravdu, funguje to. Chyba byla úplně někde jinde, jak se říká, "mezi klávesnicí a židlí."

Díky.

# March 24, 2008 12:35 PM

Weblog @ Rebex.cz :: Martin Vobr napsal:

Linky ze světa .NETu: Dnes wireshark 1.0, knihovna pokročilých generických datové struktury, seriál o stavovém HTTP, reflexe generik a licenční okénko o virtuálních CPU.

# April 25, 2008 4:09 PM
Nejsou povoleny nové komentáře k tomuto příspěvku