Reflexe umožňuje získávat metainformace o třídách, jejich vlastnostech, metodách i událostech a ty pak používat, aniž bychom museli vědět či deklarovat, o jakou konkrétní třídu se vlastně jedná. Při vhodném použití této technologie pak můžeme generovat tabulkové přehledy dat a formuláře pro editaci záznamů univerzálním způsobem tak, že jeden kód dokáže vygenerovat funkční GUI pro instanci libovolné třídy. V informačním systému používajícím tento princip pak není třeba programovat stovky oken, ale pouze vhodně nedeklarovat třídy a propojit je s databází, což víceméně automatizovaně obstará LINQ to SQL.
Reflexe
Třídy, struktury, výčty apod. většinou deklarujeme a používáme přímo pro konkrétní případy použití. To je samozřejmě v pořádku. Vytvoříme si například třídu Osoba s vlastnostmi jako Jmeno, Prijmeni, DatumNarozeni atd. (viz kód níže), pro načtení hodnot do instancí této třídy, ať již zadáním, ze souboru, z databáze, z internetu..., použijeme konstruktor (var osoba = new Osoba();) a přímý přístup k jejím vlastnostem (např. osoba.Jmeno = "Alois";).
namespace Reflexe
{
public class Osoba
{
public string Jmeno { get; set; }
public string Prijmeni { get; set; }
public DateTime DatumNarozeni { get; set; }
public int Vyska { get; set; }
}
}
Pro zobrazení tohoto seznamu pak vytvoříme zvláštní okno (nebo webovou stránku, mobilní obrazovku atd.) s tabulkou (Grid, Table...) či přehledem (ListView, DataList...) a pro zobrazení detailních informací popř. editaci konkrétního záznamu nějaký formulář (form, FormView, DetailView, Grid či StackLayout s editory; záleží na použité technologii), s individuálním editorem pro každou vlastnost. Máme-li nějaký rozsáhlejší systém třeba s dvaceti takovýmito třídami, pak je pro každou z nich obvykle nezbytné tato dvě okna (přehled a detail) navrhnout, vytvořit, odladit a udržovat. To už je nějakých 40 oken (či stránek) + úvodní nabídka s menu popř. nějaká pokročilejší navigace. Co kdyby se ale tato okna vytvářela sama?
Existuje totiž, alespoň tedy v .NET C#, sytém zvaný Reflexe (Reflection), který umožňuje pracovat s metadaty tříd, struktur atd., obecně tedy datových typů (Type). Vše potřebné se nachází ve jmenném prostoru (namespace) System.Reflection. Funguje to jak v klasickém .NET tak i v Xamarin.Forms, tedy u multiplatformních aplikací (UWP, Android, iOS), byť s drobnými odlišnostmi v syntaxi (v XF třída Type nemá všechny potřebné atributy, ty jsou skryty až ve třídě TypeInfo). Tyto rozdíly shrnuje následující tabulka.
| Popis | .NET | Xamarin.Forms |
|---|---|---|
| Typ | ||
| Název třídy s informacemi (metadaty) o třídě, struktuře atd. | Type | TypeInfo |
| Získání Type/TypeInfo z objektu (instance třídy) | Type t = osoba.GetType(); | TypeInfo t = osoba.GetType().GetTypeInfo(); |
| Získání Type/TypeInfo ze třídy | Type t = typeof(Osoba); | TypeInfo t = typeof(Osoba).GetTypeinfo(); |
| Konstruktor | ||
| Vytvoření instance třídy z Type/TypeInfo (přes bezparametrický konstruktor) | var c = GetConstructor(new Type[]{}); var osoba = c.Invoke(null); |
var osoba = new Activator.CreateInstance(t); |
Další tabulka už pak ukazuje jednotný kód, protože je pro .NET i Xamarin.Forms shodný. Rozdíl je pouze v získání typu t (viz předchozí tabulka), který je buď Type v .NET nebo TypeInfo v Xamarin.Forms.
| Popis | Kód |
|---|---|
| Typ | |
| Název typu | string nazev = t.Name; |
| Celý název typu (včetně namespace) | string nazev = t.FullName; |
| Vlastnosti | |
| PropertyInfo konkrétní vlastnosti daného názvu | var prop = t.GetProperty("Jmeno"); |
| Název vlastnosti | string nazev = prop.Name; |
| Datový typ vlastnosti | Type tp = prop.PropertyType; |
| Je datový typ vlastnosti string? | bool isString = tp == typeof(string); |
| Je vlastnost pro čtení (má get kód), a pro zápis (má set kód)? |
bool hasGet = prop.CanRead; bool hasSet = prop.CanWrite; |
| Získání hodnoty vlastnosti daného objektu | var jmeno = prop.GetValue(osoba); |
| Nastavení hodnoty vlastnosti danému objektu | prop.SetValue(osoba, "Alois"); |
| Seznam vlastností (PropertyInfo) třídy včetně vlastností předků | var props = t.GetProperties(); |
| Seznam pouze veřejných (public) vlastností | var props = t.GetProperties(BindingFlags.Public); |
| Metody | |
| MethodInfo konkrétní nepřetížené metody daného názvu | var meth = t.GetMethod("GetJmeno"); |
| MethodInfo konkrétní přetížené metody daného názvu a typů vstupních parametrů | var meth2 = t.GetMethod("SetJmeno", new Type[] {typeof(string)}); |
| Typ návratové hodnoty metody | Type rt = meth.ReturnType; |
| Spuštění metody: bezparametrické, se získáním návratové hodnoty, se vstupními parametry |
meth.Invoke(this, new object[] {}); var result = meth.Invoke(this, new object[] {}); meth2.Invoke(this, new object[] {"Alois"}); |
| Assembly | |
| Získání Assembly (informací o celém projektu) | Assembly a = t.Assembly; |
| Získání Assembly hlavního spouštěncího (executing/Startup) projektu | Assembly a = Assembly.GetExecutingAssembly(); |
| Všechny typy v projektu | Type[] types = a.GetTypes(); |
| Typ daného názvu (celého názvu, včetně namespace "Reflexe") | Type t = a.GetType("Reflexe.Osoba"); |
| Typ daného názvu (jen názvu třídy, bez namespace; název by se neměl v projektu opakovat, nebo by to mohlo vrátit typ jiné třídy než očekáváme) | Type t = a.GetTypes().FirstOrDefault(x => x.Name == "Osoba"); |
| Atributy | |
| Všechny vlastní atributy člena třídy či třídy samotné (mi = prop/meth/t/a/...; mi - MemberInfo je předek PropertyInfo, MethodInfo, Type, Assembly...) | IEnumerable<CustomAttributeData> atrs = mi.CustomAttributes; |
| Všechny vlastní atributy člena daného typu (třídy) T | IEnumerable<T> atrs = mi.GetCustomAttributes<T>(); |
| Vlastní atribut člena daného typu (třídy) T, je-li jen jeden | T atr = mi.GetCustomAttribute<T>(); |