.NET - Úvod do Reflection
Co tento článek ukazuje
- Základní přehled jmenného prostoru
System.Reflection
- Procházení typů v assembly a zjišťování detailů o typu
- Zjišťování informací o metodách a vlastnostech třídy
- Vytváření tříd a volání metod pomocí Reflection za běhu
Reflection je součást .NET Frameworku, pomocí které lze v .NETu
pracovat s třídami o kterých v době kompilace nic nevíme. Pomocí reflection
lze dynamicky načítat objekty z assemblies (tedy dll
nebo exe
souborů), zjišťovat informace
o těchto objektech, jako například seznam vlastností a metod, parametry jednotlivých
metod a v neposlední řadě lze tyto metody také volat.
To, že potřebujete pracovat s úplně neznámým objektem není úplně typická situace i pokud načítáte objekty dynamicky, protože obvykle znáte bázovou třídu objektu a nebo interface, který implementuje a pomocí této bázové třídy či interface s objektem pracujete (viz například starší tutoriál o tvorbě aplikací umožňující tvorbu pluginů [1]). Reflection ale může být občas potřeba a jedná se o velmi mocnou zbraň.
Typy v assembly
Assembly v .NETu odpovídá dll
nebo exe
souboru
a lze s ní pracovat pomocí třidy Assembly
. Tato třída obsahuje
statické metody, které umožňují načítat assembly ze souboru a nebo získat
aktuální assembly. Třídy jsou uloženy přímo v assembly a jsou pojmenovány celým
jménem včetně jména namespace. Následující kus kódu načte assembly ze souboru
Test.dll
a pomocí metody GetTypes
získá informace o všech
typech v assembly (získá objekt System.Type
, který popisuje
datový typ) a pro každý vypíše jméno a o jaký datový typ se jedná
(třída, struktura, atd..):
// nacte assembly ze souboru Assembly asm=Assembly.LoadFile("Test.dll"); // prochazi typy v assembly foreach(Type t in asm.GetTypes()) { string type="class"; // delegate je ve skutecnosti trida a lze jej poznat // tak ze je potomkem Deleagete nebo MilticastDelegate if (t.BaseType!=null&&(t.BaseType.FullName=="System.Delegate" ||t.BaseType.FullName=="System.MulticastDelegate")) type="delegate"; // Vyctovy datovy typ else if (t.IsEnum) type="enum"; // Rozhrani else if (t.IsInterface) type="interface"; // Hodnotovy datovy typ - struktura else if (t.IsValueType) type="struct"; // Vypise typ a jeho jmeno Console.WriteLine("{0} {1}",type,t.FullName); }
Informace o typu
V předcházející ukázce jsme u každého typu v assembly vypisovali
pouze o jaký typ se jedná a jméno, ale pomocí objektu
System.Type
lze dělat mnohem více. Tento objekt obsahuje
metody GetConstructors
, GetMethods
,
GetProperties
a další podobné, pomocí kterých lze získat
informace o rozhraní datového typu. V následující ukázce se podíváme,
jek lze získat seznam metod a jejich parametrů.
// Pomoci GetMethods ziska seznam metod foreach(MethodInfo mi in type.GetMethods()) { // Vynechame specialni metody (napr. pro kazdou // vlastnost existuje metoda set_Xyz a get_Xyz) if (mi.IsSpecialName) continue; // pomoci GetParameters ziska parametry metody string ps=""; ParameterInfo[] p=mi.GetParameters(); for(int i=0; i<p.Length; i++) { if (i!=0) ps+=","; ps+=p[i].ParameterType.Name+" "+p[i].Name; } // Vypise deklaraci metody s parametry Console.WriteLine("{0}({1})",mi.Name,ps); }
Volání pomocí Reflection
V poslední části tohoto tutoriálu se podíváme jak lze dynamicky vytvořit objekt někajé třídy pokud známe její jméno a assembly a jak lze volat pomocí reflection metody tohoto objektu podle jména. Nejprve kód třídy se kterou budeme později dynamicky pracovat:
// Testovaci trida class Zamestnanec { private string _jmeno,_prijmeni; private int _plat; // Vytvori zamestnance public Zamestnanec(string jmeno,string prijmeni) { _plat=10000; _jmeno=jmeno; _prijmeni=prijmeni; } // Zjistuje plat zamestnance public int Plat { get { return _plat; } } // Zmeni zamestnancovi plat public void ZmenitPlat(float nasobek) { _plat=(int)(nasobek*(float)_plat); } }
K vytvoření objektu
použijeme statickou metodu CreateInstance
třídy Activator
,
která vytváří instanci objektu podle informací o objektu (System.Type
).
Při vytváření objektu lze předat metodě CreateInstance
pole objektů
podle kterých se při vytváření vybere odpovídající konstruktor (pokud žádný
vhodný neexistuje dojde k vyjímce). Poté pomocí GetMethod
získáme
objekt typu MethodInfo
popisující metodu ZmenitPlat
a
tuto metodu zavoláme. Nakonec ještě obdobným způsobem zjistíme novou hodnotu
vlastnosti Plat
.
// zjistime aktualni assembly Assembly asm=Assembly.GetExecutingAssembly(); // zjisti informace o typuzamestnanec Type zamType=asm.GetType("ReflectionDemo2.Zamestnanec"); // vytvori instanci zamestnance // obdoba: objZam=new Zamestnanec("Bill","Gates"); object objZam=Activator.CreateInstance(zamType, new object[] {"Bill","Gates"} ); // Volani metody - zmena plat u // obdoba: objZam.ZmenitPlat(2.0f); MethodInfo met=zamType.GetMethod("ZmenitPlat"); met.Invoke(objZam,new object[] {2.0f}); // Zjisteni hodnoty vlastnosti // obdoba: Console.WriteLine(objZam.Plat); PropertyInfo propPlat=zamType.GetProperty("Plat"); int plat=(int)propPlat.GetValue(objZam,null); Console.WriteLine("Plat={0}",plat);
Ukázkové aplikace
V přiložených ukázkách naleznete tentokrát dvě aplikace. První (ReflectionDemo - viz obrázek) načítá základní standardní assembly .NET Frameorku a vytváří stromovou strukturu, kde jsou třídy zařazeny podle namespace. U každé třídy je také možno zobrazit její metody, vlastnosti a další. Jedná se o poměrně jednoduchý nástroj, ale co vše lze pomocí reflection dosáhnout demonstruje například nástroj pojmenovaný .NET Reflector [2]. Druhý ukázkový projekt (ReflectionDemo2) obsahuje mírně rozšířený příklad z poslední části tohoto článku.
Soubory na stažení a odkazy
- [1] WinForms - Aplikace s podporou pluginů [^] - Vyvojar.cz
- [2] .NET Reflector [^] - Lutz Roeder