TP

.NET - Úvod do Reflection

Ukázková aplikace

Co tento článek ukazuje

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

Published: Sunday, 19 June 2005, 12:07 AM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: