Reflection
Wat is reflection?
Reflection maakt het mogelijk dat een programma zijn eigen structuur kan bekijken en zo mogelijk aanpassen.
Reflection is een onderdeel van het .NET framework (via de System.Reflection namespace). Met behulp van reflection kunnen we tijdens runtime programma-klassen bekijken, objecten instantieren, waarden wijzigen etc…
Het nut van reflection is bijvoorbeeld dat er gemakkelijker generieke code kan worden geschreven. Met behulp van reflection kan bijv. voor onderstaande of soortgelijke klassen SQL queries worden opgebouwd die de alle noodzakelijke communicatie naar een willekeurig DBMS mogelijk maken. Dit hoeft dan niet meer per klasse te gebeuren. NHibernate is een goed voorbeeld van gebruik van o.a. reflection.
Aan de hand van onderstaande klasse wil ik laten zien hoe het een ander met behulp van reflection gedaan kan worden.
Persoon.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/// <summary> /// Summary description for Persoon /// </summary> public class Persoon { private Guid _Id; private string _Voornaam; private string _Achternaam; private DateTime _GeboorteDatum; #region Constructors public Persoon() { _Id = Guid.NewGuid(); } public Persoon(string Voornaam, string Achternaam) : this() { _Voornaam = Voornaam; _Achternaam = Achternaam; } #endregion #region Properties public Guid Id { get { return _Id; } set { _Id = value; } } public string Voornaam { get { return _Voornaam; } set { _Voornaam = value; } } public string Achternaam { get { return _Achternaam; } set { _Achternaam = value; } } public DateTime GeboorteDatum { get { return _GeboorteDatum; } set { _GeboorteDatum = value; } } #endregion public string GetFullname() { return string.Format("{0}, {1}", _Achternaam, _Voornaam); } } |
De System.Type klasse is zo’n beetje de belangrijkste bij het gebruik van reflection. Om aan een System.Type instantie te komen kan op een instantie van een klasse (object) de .GetType() methode worden aangeroepen. Is er geen instantie beschikbaar dan kan doormiddel van typeof(..) functie het System.Type van een bepaald type worden opgevraagd.
1 2 3 4 5 6 |
Persoon p = new Persoon(); System.Type PersoonType = p.GetType(); of System.Type PersoonType = typeof(Persoon); |
Nu een System.Type ter beschikking staat kunnen gegevens worden opgevraagd, bijvoorbeeld:
1 2 3 4 5 |
PersoonType.Name PersoonType.FullName PersoonType.Module.FullyQualifiedName PersoonType.Namespace PersoonType.UnderlyingSystemType.ToString() |
resulteert in:
Name | : | Persoon |
FullName | : | snoei.net.test.Persoon |
Module.FullyQualifiedName | : | C:WINDOWSMicrosoft.NETFrameworkv2.0.50727Temporary ASP.NET Filesprostat124b611274f8497bApp_Code.jtzqqm81.dll |
Namespace | : | snoei.net.test |
UnderlayingSystemType | : | snoei.net.test.Persoon |
Properties
De properties van de Persoon klasse kunnen alsvolgt worden uitgevraagd:
1 |
PersoonType.GetProperties(); |
Deze retourneert een array met PropertyInfo objecten. Het PropertyInfo object beschrijft een property.
Met de volgende code kunnen we propertyinformatie ophalen:
1 2 3 4 5 6 7 8 |
foreach (PropertyInfo pi in PersoonType.GetProperties()) { Console.WriteLine(string.Format("Name: {0} Type: {1} Getter: {2} Setter: {3}", pi.Name.PadRight(15), pi.PropertyType.ToString().PadRight(15), pi.CanRead.ToString().PadRight(6), pi.CanWrite.ToString().PadRight(6))); } |
Het resultaat:
1 2 3 4 |
Name: Id Type: System.Guid Getter: True Setter: True Name: Voornaam Type: System.String Getter: True Setter: True Name: Achternaam Type: System.String Getter: True Setter: True Name: GeboorteDatum Type: System.DateTime Getter: True Setter: True |
Middels het PropertyInfo object is het mogelijk propertygegevens te lezen en te schrijven voor een bepaalde Persoon instantie.
1 2 3 4 5 6 7 8 9 10 11 12 |
1. Persoon p = new Persoon(); 2. p.Voornaam = "Ton"; 3. p.Achternaam = "Snoei"; 4. p.GeboorteDatum = new DateTime(1973, 10, 9 ); 5. System.Type PersoonType = typeof(Persoon); 6. PropertyInfo piVoornaam = PersoonType.GetProperty("Voornaam"); 7. object value = piVoornaam.GetValue(p, null); 8. Console.WriteLine("Waarde van Property Voornaam van Persoon object p: " + (string) value); 9. piVoornaam.SetValue(p, "Pietje", null); 10. Console.WriteLine("Waarde van Property Voornaam van Persoon object p: " + p.Voornaam); |
1 |
p.GetType() |
zou in hetzelfde resulteren.
Dit resulteert in het volgende:
1 2 |
Waarde van Property Voornaam van Persoon object p: Ton Waarde van Property Voornaam van Persoon object p: Pietje |
Methoden
Naast het uitvragen van properties kunnen ook methodes worden uitgevoerd. Doormiddel van de functie PersoonType.GetMethods() kunnen we gegevens van alle methoden opvragen. Deze functie retourneert een array van MethodInfo objecten.
Het persoon object heeft een methode “GetFullname”. Met behulp van de volgende code roepen we deze methode aan:
1 2 |
MethodInfo mi = PersoonType.GetMethod("GetFullname"); mi.Invoke(p, new object[] { }); |
Met behulp van de Invoke functie kan de methode worden aangeroepen. Het betrokken object moet worden meegegeven in het geval het niet om een static method gaat. Gaat het om een static method dan wordt dan ziet de invoke er alsvolgt uit:
1 |
mi.Invoke(null, new object[]{ }); |
Aangezien de methode geen parameters heeft wordt een lege object array meegegeven. Mochten de methode parameters bevatten dan kunnen de mee te geven waarden (in de juiste volgorde) in de object array worden geplaatst. Stel dat we een GetFullname methode hebben die een string verwacht waarmee titel kan worden gezet dan ziet de code er alsvolgt uit:
1 2 |
MethodInfo mi = PersoonType.GetMethod("GetFullname"); mi.Invoke(p, new object[] {"Drs."}); |
Instantiatie
Er is uiteraard nog veel meer mogelijk met reflection. Maar voor nu wil ik alleen nog even laten zien hoe er een instantie van een klasse kan worden gemaakt via reflection. Dus het maken van een instantie van de Persoon klasse in runtime:
1 |
Persoon p = (Persoon) Activator.CreateInstance(typeof(Persoon)); |
Conclusie
Dat is reflection in het kort. In veel gevallen kan het gebruik van reflection zaken generieker maken en maakt oplossingen met minder code mogelijk. Reflection is een redelijk alternatief voor codegeneratie. Dat wil echter niet zeggen dat codegeneratie in alle gevallen vervangen kan worden door reflection te gebruiken. Een nadeel van reflection is het ten koste gaat van enige performance. De performance is echter voor veel toepassingen ruim toereikend en dan hoeft het geen belemmering te zijn.
P.J. van de Sande
Het belangrijkste vergeet je imho, Attributes. Deze worden veel gebruikt en dienen uitgelezen te worden met reflecten.
Volgende post over IL Emitting? 🙂
tsnoei
Tja, je hebt gelijk, dat had er nog wel bij gemogen.
IL emitting is wel een vak apart ik heb daar eerlijk gezegd nog niet zo heel veel mee gedaan.