Αυτό το post είναι μέρος μιας τετράδας από blog posts σχετικά με το reflection. Δείτε τα υπόλοιπα εδώ
Στο προηγούμενο blog post αναφερθήκαμε σχετικά με τι είναι γενικά το reflection, και πως μπορούμε να πάρουμε διάφορες πληροροφίες από μία assembly κατά το runtime. Σε αυτό, θα δούμε πως μπορούμε να πάρουμε πληροφορίες για διάφορα types και να κάνουμε reflection πάνω σε αυτά.
Getting Types
Υπάρχουν διάφοροι τρόποι για να πάρουμε types.
- Μπορούμε να πάρουμε τα types μιας assembly
- Μπορούμε να πάρουμε τα types ενός module
- Μπορούμε να πάρουμε τα types κάποιου instance ενός object
- Μπορούμε να χρησιμοποιήσουμε τα keywords typeof (C#) και GetType (Visual Basic)
Όταν εργαζόμαστε με μία assembly, μπορούμε να πάρουμε τα Types της με χρήση της GetTypes() συνάρτησης. Η ίδια μέθοδος υπάρχει και στο αντικείμενο Module.
Επίσης, μπορούμε να πάρουμε το Type ενός instance ενός αντικειμένου, με την GetType μέθοδο (την κληρονομεί από το System.Object)
Επίσης, μπορούμε να δημιουργήσουμε ένα Type από ένα value type ή ένα reference type με τη χρήση της typeof keyword (GetType στη VB)
Το αντικείμενο Type έχει πολλά μέλη (συναρτήσεις, properties κ.λ.π.) τα οποία μπορείτε να τα δείτε εδώ. Μερικές πληροφορίες που μπορούμε να πάρουμε για ένα instance ενός Type είναι το namespace του, το όνομά του, αν είναι ValueType (όπως τα int, float, double, κάποιο structure κ.λ.π.), αν είναι Public κ.λ.π. Δείτε τον παρακάτω κώδικα για μερικές από τις πληροφορίες που μπορούμε να πάρουμε
Επιπροσθέτως, με τον ίδιο τρόπο που μπορούμε να εξετάσουμε για attributes σε μία assembly, μπορούμε να εξετάσουμε και για attributes σε ένα Type
Η διαφορά ανάμεσα στην GetCustomAttributes όταν εκτελείται σε assembly και όταν εκτελείται σε Type, είναι πως η boolean παράμετρος ενώ αγνοείται όταν γίνεται κλήση από assembly, εδώ δεν αγνοείται. Η χρησιμότητά της είναι ότι “λέει” στην GetCustomAttributes να κοιτάξει στην κλάση που αντιπροσωπεύει το instance του Type και για attributes που γίνονται inherit από μία γονική κλάση.
Σημείωση |
Έστω ότι έχουμε τα εξής ![]() Έτσι και εκτελέσουμε τον παρακάτω κώδικα ![]() το αποτέλεσμα θα είναι ![]() Γιατί; Επειδή ένα αντικείμενο είναι πάντα ενός τύπου. Μπορεί να γίνει cast σε μία γονική κλάση ή σε ένα Interface που υλοποιεί, αλλά ο τύπος του δεν αλλάζει. |
Enumerating Class members
Στην κλάση Type υπάρχουνε μέθοδοι για να πάρουμε διαφορετικά μέρη ενός Type (μεθόδους, properties, fields, events κ.α.). Κάθε ένα από αυτά τα μέρη αντιπροσωπεύεται στο Framework από μία κλάση που το όνομά της τελειώνει με τη λέξη Info. Έτσι λοιπόν, η κλάση που αντιπροσωπεύει μια μέθοδο λέγεται MethodInfo, η κλάση που αντιπροσωπεύει ένα Event λέγεται EventInfo, κ.λ.π. Όλες αυτές οι κλάσεις παράγονται από μία γονική, που ονομάζεται MemberInfo (αυτή την κλάση κληρονομεί και το Type αντικείμενο). Στην κλάση MemberInfo υπάρχουν κάποιες κοινές μέθοδοι για διάφορες πληροφορίες, ενώ οι σημαντικές πληροφορίες υπάρχουνε στις κλάσεις “απογόνους”. Οι συναρτήσεις για να πάρουμε instances αυτών των κλάσεων από ένα Type ακολουθούν την ονοματοδοσία Get + Event|Method|Field|Property κ.α. Στο τέλος μπορεί να υπάρχει και η κατάληξη s, για να μας επιστραφεί ένας πίνακας από objects.
Για παράδειγμα, για να βρούμε όλα τα methods σε μία κλάση κάνουμε το εξής
Βλέπουμε ότι μπορούμε να πάρουμε το όνομα κάθε συνάρτησης, το όνομα της επιστρεφόμενης τιμής της, καθώς και όλα τα ονόματα των παραμέτρων της.
Είναι πολύ εύκολο επίσης να δούμε και τα Nested Types
αλλά και τα Members γενικά
Βέβαια, όσο κάνουμε enumerate τα MemberInfo, μπορούμε να δούμε και αν κάποιο από αυτά είναι κάποιου συγκεκριμένου τύπου, κάνοντας χρήση του MemberTypes enumeration
Το MethodBody
Για να πάρουμε τον IL κώδικα μιας μεθόδου, χρησιμοποιούμε το MethodBody αντικείμενο (επιστρέφεται από τη συνάρτηση GetMethodBody, η οποία υπάρχει στις κλάσεις ConstructorInfo ή MethodInfo).
Δείτε εδώ πως μπορούμε να δούμε τις τοπικές μεταβλητές μιας συνάρτησης αλλά και πως μπορούμε να πάρουμε τον IL κώδικα της συνάρτησης σε έναν πίνακα από bytes.
BindingFlags
Το enumeration BIndingFlags χρησιμοποιείται για να ελέγξει πως κάποια members ενός Type λαμβάνονται κατά τη χρήση της GetMembers μεθόδου. Είναι ένα flagged enumeration, πράγμα το οποίο σημαίνει ότι δύο ή περισσότερα μέλη της μπορούν να χρησιμοποιηθούν ταυτόχρονα.
Τα μέλη του BindingFlags enumeration είναι
- DeclaredOnly : αγνοούνται τα inherited members
- Default : Δεν χρησιμοποιείται κάποιο BindingFlag
- FlattenHierarchy : Επιστρέφει declared, inherited αλλά και protected members
- IgnoreCase : Αγνοείται το casing (πεζά – κεφαλαία) κατά το ταίριασμα του ονόματος
- Instance : Επιστρέφονται τα μέλη του instance του type
- NonPublic : Επιστρέφει protected, internal και private μέλη
- Public : Επιστρέφει τα public μέλη
- Static : Επιστρέφει τα static μέλη
Δείτε τον παρακάτω κώδικα
Ο μέχρι τώρα κώδικας επισυνάπτεται στο παρόν post.
Στο επόμενο blog post θα δούμε την χρήση κώδικα από assemblies που γίνονται reflected στο runtime.