Αυτό το post είναι μέρος μιας τετράδας από blog posts σχετικά με το reflection. Δείτε τα υπόλοιπα εδώ
Το συγκεκριμένο blog post ξεκινάει από μια κουβέντα που είχα σε ένα άλλο forum, όπου μου ζητήθηκε να δώσω μερικές πληροφορίες για το Reflection στο .ΝΕΤ. Well, here it is!
Το reflection στο .ΝΕΤ χρησιμοποιείται για να πάρουμε πληροφορίες σχετικά με τους τύπους/κλάσεις μιας assembly αλλά και για να δημιουργήσουμε κώδικα “on-the-fly”. Το αποτέλεσμα είναι ο κώδικας του επερωτούμενου assembly παρουσιαζόμενος σε ένα οbject model.
Τι είναι η assembly και τι το module;
Ο κώδικας στο CLR “πακετάρεται” σε μία assembly. Εκτός από τον κώδικα, περιλαμβάνονται μεταδεδομένα (metadata) τα οποία το CLR χρησιμοποιεί για να φορτώσει και να εκτελέσει τον κώδικα, αλλά και πληροφορίες για όλους τους τύπους (types) μέσα στην assembly. Το τελευταίο περιλαμβάνει κάθε μέθοδο, property, event, delegate και interface κάθε τύπου. Επιπροσθέτως, υπάρχουν μεταδεδομένα για τον ίδιο τον κώδικα, όπως το μέγεθός του αλλά και τις τοπικές μεταβλητές. Ουσιαστικά, μία assembly είναι ένας λογικός χώρος όπου μέσα φυλάσσονται διάφορες πληροφορίες, και πιο συγκεκριμένα τα εξής
· Assembly metadata
· Type metadata
· IL Code
· Resources
Τα assembly metadata (αλλιώς καλούνται και manifest) περιγράφουνε την ίδια την assembly, όπως το όνομά της, την έκδοση, το culture κ.λ.π. Μπορείτε να δείτε ένα dump του manifest μιας Console εφαρμογής ονόματι Reflection (χρησιμοποιώντας το ILDasm.exe utility) εδώ
Type metadata είναι όλες οι πληροφορίες που περιγράφουνε πως είναι ένα type (το namespace, το όνομα του type, επιπρόσθετα types που περιέχει, τα μέλη του, όπως συναρτήσεις, delegates, enumerations κ.λ.π.). Για παράδειγμα, για μία κλάση ονόματι MyClass στο ίδιο με το προηγούμενο assembly (ονόματι Reflection.exe) που έχει ένα public property MyInt που κάνει encapsulate έναν private integer _MyInt, το type metadata δείχνει κάπως έτσι
Ο IL Code, είναι ο μεταγλωττισμένος σε IL κώδικας της εφαρμογής. Επί παραδείγματι, για τη Main συνάρτηση της Console εφαρμογής μας, ο κώδικας σε C# είναι
και σε IL βλέπουμε ότι έχει γίνει
Τα Resources είναι διάφορα αντικείμενα που έχουνε γίνει embed στην assembly και μπορούν να χρησιμοποιηθούν από την εφαρμογή (π.χ. images, icons κ.α.).
Τις περισσότερες φορές, μία assembly περιλαμβάνεται σε ένα και μόνο αρχείο. Κάποιες φορές όμως, μπορεί να θελήσουμε μία assembly να “σπάσει” σε περισσότερα από ένα αρχεία (να σημειωθεί ότι στο Visual Studio 2005 δεν υπάρχει εγγενής υποστήριξη για να γίνει αυτό). Σε αυτή την περίπτωση ένα κεντρικό αρχείο περιέχει τα assembly metadata ενώ τα type metadata, code και resources μπορεί να είναι είτε στο κεντρικό αρχείο είτε στα υπόλοιπα αρχεία της assembly (τα οποία έχουν την προέκταση .netmodule)
Από την άλλη, τα modules είναι containers για τύπους μέσα σε μια assembly. Δεν χρησιμοποιείται συνήθως, παρά μόνο σε ειδικές περιπτώσεις όπως π.χ. σε ανάμιξη διαφορετικών γλωσσών μέσα σε μία assembly. Το Visual Studio δεν υποστηρίζει πολλαπλά modules σε μία assembly, έτσι η δημιουργία multimodule asemblies γίνεται με ειδικά εργαλεία (MSBuild).
Εξέταση μιας assembly
Για να ξεκινήσουμε να εξετάσουμε μία Assembly, πρέπει να δημιουργήσουμε ένα instance της κλάσης Assembly (αφού κάνουμε using το System.Reflection). Ας δούμε μερικά πράγματα μπορούμε να κάνουμε με αυτό. Ο κάτωθι κώδικας
μας δίνει σαν αποτέλεσμα τα ακόλουθα
Η GetExecutingAssembly χρησιμοποιείται για να δώσει ένα reference στην εκτελούμενη assembly.
Με τη χρήση της Assembly.ReflectionOnlyLoad(string fullName) και της Assembly.ReflectionOnlyLoadFrom(string path) μπορούμε να διαβάσουμε τα δεδομένα μιας άλλης assembly, αλλά δεν επιτρέπεται να χρησιμοποιήσουμε κώδικα από την συγκεκριμένη assembly ούτε να δημιουργήσουμε νέα instances τύπων που περιλαμβάνονται στην συγκεκριμένη assembly. Αυτό γίνεται γιατί απαιτείται αρκετή δουλειά από το CLR για να φορτωθεί ένας καινούριος τύπος στο τρέχων εκτελούμενο ApplicationDomain, οπότε αυτές οι δύο συναρτήσεις έχουνε προστεθεί για μεγαλύτερη απόδοση. Για να φορτώσουμε μία assembly προκειμένου να χρησιμοποιήσουμε κώδικά της, μπορούμε να χρησιμοποιήσουμε τις μεθόδους Load(string assemblyString), LoadFile(string path), LoadFrom(string assemblyFile).
Με τον κάτωθι τρόπο μπορούμε να πάρουμε διάφορες πληροφορίες για τα modules μιας assembly
Μπορείτε να δείτε περισσότερες πληροφορίες για τις κλάσεις Assembly και Module εδώ και εδώ
Assembly Attributes
Τα assembly attributes είναι ιδιότητες που μπορούν να παρέχουν περισσότερες πληροφορίες για μία assembly. Τυπικά, αυτές βρίσκονται σε ένα αρχείο ονόματι AssemblyInfo.cs (AssemblyInfo.vb στη Visual Basic). Μπορείτε να δείτε μερικές εδώ
Κατασκευάζονται με τη χρήση του assembly prefix, μετά με ένα string που αναπαριστάνει την εκάστοτε κλάση που θα δοθεί τιμή (για παράδειγμα, το string AssemblyCompany θα δώσει τιμή στην κλάση AssemblyCompanyAttribute). Με αυτόν τον κώδικα μπορούμε εύκολα να κάνουμε enumerate αυτές τις attributes.
Αν θέλουμε να πάρουμε τιμή για κάποια συγκεκριμένη attribute, μπορούμε να χρησιμοποιήσουμε τον παρακάτω κώδικα
όπου και παίρνουμε τιμή για το FileVersionAttribute. Stay tuned για το επόμενο blog post, όπου και θα δείξουμε πως να κάνουμε reflection σε Types.
Ο μέχρι στιγμής κώδικας είναι συνημμένος στο παρόν blog post.