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>(); |