Reflection, part Four

Αυτό το post είναι μέρος μιας τετράδας από blog posts σχετικά με το reflection. Δείτε τα υπόλοιπα εδώ

Σε αυτό το blog post θα δούμε πώς να δημιουργήσουμε τα δικά μας Types ή assemblies κατά το runtime (είτε για να τις αποθηκεύσουμε στο σκληρό δίσκο είτε για να τις χρησιμοποιήσουμε ενώ είναι στη μνήμη).

 

Κατασκευή δικού μας κώδικα

Το σύστημα του Reflection περιλαμβάνει ένα υπο-namespace το οποίο ονομάζεται Emit (System.Reflection.Emit) που περιλαμβάνει κάποιες κλάσεις που χρησιμοποιούνται για κατασκευή Assemblies, Types, Methods κ.λ.π. Για να κατασκευάσουμε κώδικα κατά το runtime, πρέπει να τον κάνουμε encapsulate όπως συνήθως (κατασκευή assembly, κατασκευή module μέσα στην assembly, και μετά κατασκευή Types μέσα στο Module). Κάθε μία από αυτές τις κλάσεις έχει παρόμοιο όνομα με την κλάση που κατασκευάζει, μαζί με την προσθήκη της λέξης Builder (για παράδειγμα για κατασκευή μιας Assembly χρησιμοποιούμε την κλάση AssemblyBuilder). Οι Builder κλάσεις που υποστηρίζονται είναι οι εξής

Όνομα κλάσης

Κατασκευάζει

AssemblyBuilder

assemblies

ConstructorBuilder

constructors

EnumBuilder

enumerations

EventBuilder

events

FieldBuilder fields
LocalBuilder τοπικές μεταβλητές για συναρτήσεις και constructors
MethodBuilder μεθόδους / συναρτήσεις
ModuleBuilder modules
ParameterBuilder παραμέτρους
PropertyBuilder properties
TypeBuilder types

Δημιουργία Assembly και Module

Πριν την δημιουργία ενός Type, πρέπει να δημιουργήσουμε μία Assembly και ένα Module. Για να το κάνουμε αυτό, πρέπει να ζητήσουμε από ένα AppDomain να δημιουργήσει μια δυναμική assembly. Η κλάση AppDomain έχει μία μέθοδο ονόματι DefineDynamicAssembly που παίρνει σαν παραμέτρους ένα AssemblyName αντικείμενο και ένα AssemblyBuilderAccess enumeration. Το πρώτο περιλαμβάνει πληροφορίες για την assembly και το δεύτερο είναι ένα enumeration με τιμές ReflectionOnly, Run, Save, RunAndSave, όπου καθορίζει το τι θέλουμε να κάνουμε με την assembly.

 image

Εφόσον έχουμε τώρα ένα αντικείμενο AssemblyBuilder, μπορούμε να κατασκευάσουμε ένα ModuleBuilder.

image

Δημιουργία Type

Για να δημιουργήσουμε ένα νέο Type, θα καλέσουμε μια συνάρτηση του ModuleBuilder ονόματι DefineType. Αυτή η συνάρτηση παίρνει σαν παραμέτρους ένα όνομα για το Type, και μία τιμή από το enumeration TypeAttributes. Το TypeAttributes περιλαμβάνει επιλογές για το καινούριο Type.

image

Υπάρχουν διάφορες overloaded συναρτήσεις της DefineType, που επιτρέπουν (μεταξύ άλλων) το inheritance μιας άλλης κλάσης ή το implementation κάποιου interface.

Δημιουργία Members

Η κλάση TypeBuilder μας επιτρέπει να δημιουργήσουμε μέλη για το Type μας. Οι συναρτήσεις που υποστηρίζει είναι

Όνομα συνάρτησης

Δημιουργεί

DefineContructor constructor για το type
DefineDefaultConstructor constructor χωρίς παραμέτρους
DefineEvent events
DefineField fields
DefineGenericParameters generic παραμέτρους για generic types
DefineMethod συναρτήσεις
DefineMethodOverride συναρτήσεις οι οποίες κάνουν override συναρτήσεις μιας γονικής κλάσης
DefineNestedType types τα οποία είναι εμφωλευμένα (nested) σε ένα type
DefinePInvokeMethod συναρτήσεις οι οποίες καλούν εξωτερικό κώδικα μέσω της λειτουργίας Platform Invoke
DefineProperty properties

Το πρώτο πράγμα που χρειάζεται να κάνουμε για το νέο μας Type είναι να δημιουργήσουμε έναν constructor. Θα κατασκευάσουμε έναν default (χωρίς παραμέτρους) constructor, οπότε θα γράψουμε το εξής τμήμα κώδικα

image

Η συνάρτηση DefineDefaultConstructor παίρνει σαν όρισμα μία τιμή από το MethodAttributes enumeration, η οποία και καθορίζει την “ορατότητα” του νέο μας constructor. Σε αυτή την περίπτωση, επιλέγουμε να είναι public.

Κατασκευάζουμε και έναν άλλον constructor

image

Εν συνεχεία, θα χρησιμοποιήσουμε την κλάση ILGenerator προκειμένου να γράψουμε μέσα στον constructor μας κάποιο κώδικα. Αυτή η κλάση χρησιμοποιείται για να δημιουργήσουμε τοπικές μεταβλητές και IL (Intermediate Language) κώδικα. Στην προκειμένη περίπτωση, ο constructor μας απλά θα κάνει ένα return, οπότε θα γράψουμε το παρακάτω

 image

Για περισσότερες πληροφορίες σχετικά με τα OpCodes αλλά και με την Intermediate Language του .NET Framework, δείτε εδώ

Βέβαια, εκτός από constructor μπορούμε να δημιουργήσουμε και άλλες μεθόδους. Δείτε

image

Για να δημιουργήσουμε μία static μέθοδο, βάζουμε το MethodAttributes.Static στα attributes, με αυτόν τον τρόπο

image

Για να δημιουργήσουμε ένα private field, κάνουμε το εξής

image

Για να δημιουργήσουμε ένα property το οποίο θα μας κάνει expose το παραπάνω field, γράφουμε τον εξής κώδικα

image

Θα πρέπει βέβαια να φτιάξουμε και τα get και set operations για το property μας. Δείτε:

image

Τέλος, για να σώσουμε στο δίσκο την καινούρια μας assembly, κάνουμε το εξής

image

Στο παρόν blog post μπορείτε να βρείτε συννημένο τον τελικό κώδικα αυτής της τετράδας των blog posts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s