6. Reflexe

Mnohé ze služeb platformy .NET (jako pozdní vazba, serializace, vzdálené řízení, atributy a podobně) závisejí na přítomnosti metadat. Vytvářené programy mohou využívat tato metadata a rozšiřovat je o nové informace. Reflexí je označováno prozkoumávání existujících typů prostřednictvím metadat. Uskutečňuje se prostřednictvím sady typů v oboru názvů System.Reflection. Je rovněž možné dynamicky vytvářet nové typy pomocí tříd v oboru názvů System.Reflection.Emit. Reflexe představuje procházení a manipulování s objektovým modelem, který představuje nějakou aplikaci včetně všech jejích elementů kompilace a běhu.

6.1. Hierarchie typů

Základní jednotkou aplikace jsou její typy obsahující členy a vnořené typy. Typy jsou v modulech a ty obvykle v sestavách. Všechny tyto elementy jsou popsány metadaty. Za běhu jsou tyto elementy obsaženy uvnitř domény AppDomain.

Každý z prvků aplikace je vystaven prostřednictvím odpovídajícího typu z oboru názvů System nebo System.Reflection.

Obrázek 5.1. Vztahy mezi dědičnosti mezi reflexními typy .NET

Vztahy mezi dědičnosti mezi reflexními typy .NET

V okamžiku, kdy máte odkaz na některý z těchto elementů, můžete se pohybovat vztahy mezi daným elementem a souvisejícími prvky, jak je uvedeno na následujícím obrázku.

Obrázek 5.2. Pohyb hierarchií reflexe .NET

Pohyb hierarchií reflexe .NET

6.2. Typy, členy a vnořené typy

Třída Type je nejzákladnějším typem reflexe. Reprezentuje metadata pro jednotlivé deklarace typů v aplikaci. Typy obsahují členy. Ty zahrnují konstruktory, proměnné, vlastnosti, události a metody. Typy mohou navíc obsahovat vnořené typy, které se typicky používají jako pomocné třídy. Typy jsou seskupené do modulů a moduly jsou obsažené v sestavách.

6.2.1. Sestavy a moduly

Sestava je logickým ekvivalentem knihovny DLL v systému Win32 a je základní jednotkou zavádění, správy verzí a opakovaného používání typů

Modul je fyzický soubor (.exe, .dll), nebo nějaký prostředek (.gif, .jpg). Sestava může být tvořena několika moduly.

6.2.2. AppDomain

AppDomain je kořenem hierarchie typů a slouží jako kontejner sestav a typů. AppDomain je logickým ekvivalentem procesu v aplikaci Win32. Chyby aplikace způsobí zhroucení pouze té AppDomain, kde došlo k chybě.

6.3. Zjištění typu instance

V jádru systému reflexe je System.Type, což je abstraktní bázová třída poskytující přístup k metadatům nějakého typu.

K instanci třídy Type lze přistoupit pomocí GetType(). Tato metoda je implementovaná v System.Object. Po zavolání vrátí metoda konkrétní typ System.Type, který dokáže typ reflektovat a manipulovat s ním.

6.4. Přímé zjištění typu

Určitou třídu Type lze převzít pomocí názvu prostřednictvím statické metody GetType() ve třídě Type.

[ukázka kódu]
Type t = Type.GetType("System.Int32");
Type t2 = Type.GetType("MyNamespace.MyType", MyAssembly);

C# nabízí operátor typeof, který vrací třídu Type libovolného typu známého během kompilace.

[ukázka kódu]
Type t = typeof(System.Int32);

Rozdíl mezi těmito dvěma přístupy je, že přístup pomocí Type.GetType se vyhodnocuje za běhu, typeof se vyhodnocuje v době kompilace.

6.5. Reflektování hierarchie typů

Máme-li instanci Type, můžeme přistupovat k metadatům prostřednictvím typů, jež představují členy, moduly, sestavy, třídy AppDomain a vnořené typy. lze zkoumat metadata, vlastní atributy, vytvářet nové instance typů a volat členy.

Příklad používá reflexi k zobrazení členů ve třech různých typech.

[ukázka kódu]
using System;
using System.Reflection;

class Test 
{
  static void Main()
  {
    object o = new Object();
    Information(o.GetType());
    Information(typeof(int));
    Information(Type.GetType("System.String"));
  }
  static void Information(Type t)
  {
    Console.WriteLine("Type: {0}", t);
    MemberInfo[] miarr = t.GetMembers();
    foreach(MemberInfo mi in miarr)
      Console.WriteLine(" {0}={1}", mi.MemberType, mi);
  }
}

6.6. Pozdní vazba

Je dynamické vytváření instancí a používání typů za běhu.

[ukázka kódu]
//Hello.cs

public abstract  class Hello {
   public abstrakt void SayHello();
}
[ukázka kódu]
//EnglishHello.cs

using System;
public class AmericanHello : Hello
{
  private string message = "Hey Dude!";
  public override void SayHello()
  {
    Console.WriteLine(message);
  }
}
public class BritishHello : Pozdravy 
{
  private string message = "Hello!";
  public override void SayHello()
  {
    Console.WriteLine(message);
  }
}
[ukázka kódu]
//SayHello.cs

using System;
using System.Reflection;

class Test
{
  static void Main()
  {
    string s = "EnglishHello.dll";
    Assembly a = Assembly.LoadFrom(s);
    foreach (Type t in a.getTypes())
    {
      if (t.IsSubClasOf(typeof(Hello))
      {
        object o = Activator.CreateInstance(t);
        MethodInfo mi = t.GetMethod("SayHello");
            mi.Invoke(o, null);
      }
    }
  }
}

Podle řetězce s se nahraje EnglishHello.dll. Z jejich typů se vyhledá ten objekt, který má za předka třídu Hello a zajistí se nalezení metody SayHello. Metoda se vyvolá.

6.7. Vytváření nových typů za běhu

Obor názvů System.Reflection.Emit obsahuje třídy, které dokáží vytvořit za běhu úplně nové typy. Třídy umožňují:

  • definovat dynamickou sestavu v paměti;

  • definovat dynamický modul v této sestavě;

  • definovat nový typ v tomto modulu, včetně všech jeho členů;

  • vytvořit kódy MSIL potřebné k implementování aplikační logiky ve členech.