Lab 11 - Reflection

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Lab 11 - Reflection as PDF for free.

More details

  • Words: 1,756
  • Pages: 37
13

 





Codul in CLR este impachetat in assembly-uri. Metadata este data folosita de CLR pentru a incarca si executa cod, si include informatii despre clase, structuri, delegati si interfetele din un assembly. Type medatada contine informatii despre fiecare metoda, priprietate, event-uri delegati si enumerari. Exista si metadata despre codul propriuzis, incluzand marimea acestuia si variabilele locale.







Un assembly contine urmatoarele parti:  Assembly metadata  Type metadata  Cod (in IL (Intermediate Language))  Resurse Assembly metadata contine datele ce definesc assembly-ul:  Nume  Versiune  StrongName  Informatii despre cultura Assembly metadata este cunoscuta si ca manifest.



Type metadata contine informatii despre:    

 

Namespace Clase Metode, proprietati, constructori Parametrii lor

Codul este in Limbaj Intermediar (IL) ce este compilat atunci cand este executat assembly-ul Assembly-urile pot fi stocate in mai multe fisiere: puteti stoca unele portiuni din cod, type infromation si resurse in alte fisiere.

 



Modulele sunt containere de tip-uri in interiorul unui assembly. Un modul poate fi continut in un assembly single-file sau mai des este parte al unui assembly multi-file. In general veti avea mai multe modul in un singur assemby atunci cand assembly-ul este scris in mai multe limbaje de programare sau doriti suport pentru download modular.



Inainte ca sa putem examina un assembly trebuie sa cream o instanta a clasei Assembly. Aceasta contine mai multe metode statice pe care pe putem folosii pentru a crea instante.









Metodele GetCallingAssembly, GetEntryAssembly si GetExecutingAssembly permit instantierea unei clase Assembly pentru assembly-urile din stiva de apel. GetEntryAssembly intoarce o instanta a assembly-ului ce contine metoda de pornire (Main in cazul unei aplicatie consola). GetExecutingAssembly intoarce assembly-ul care se executa in timp de GetCallingAssembly intoarce assembly-ul la un nivel superior in stiva de apel. Odata ce ati incarcat un assembly in o clasa Assembly puteti interoga diferite proprietati ale acesteia. (multe metode  vedeti pagina 834-835 in carte)



Puteti afla (printre altele)  Numele complet al assemby-ului  Locatia assembly-ului



    

Clasa assembly permite incarcarea unui assembly doar pentru interogarea informatiilor sau pentru crearea de tip-uri si executarea de cod. Daca doriti doar sa cititi informatiile din assembly veti folosii metodele ReflectionOnlyLoad respectiv ReflectionOnlyLoadFrom Fiecare assembly contine unul sau mai multe module. Informatiile despre module sunt incapsulate in clasa Module. Puteti obtine modulele din un assembly folosind metoda GetModules() din clasa Assembly, care intoare un array de module. Clasa Module poate fi folosita pentru a extrage informatii despre tipurile continute in un anumit modul. (metode, variabile, proprietati)





Folosind atribute puteti schimba unele proprietati ale assemby-ului. AssembyAlgorithmIdAttribute  Este folosit pentru a specifica ce algoritm de hash este folosit pentru a citii hash-ul fisierelor din manifestul assemby-ului  [assembly: AssembyAlgorithmId(AssemblyHashAlgorithm.MD5)]



AssemblyCompanyAttribute  Este folosit pentru a specifica numele companiei care a produs assembly-ul. Este folosit de compilator pentru a pune numele companiei in header-ul DLL-urilor.  [assembly:AssemblyCompany(“ViPrAd Corporation :D”)]



AssemblyConfigurationAttribute  Specifica modul de compilare al assemby-ului: DEBUG sau RELEASE  [assemby: AssemblyConfiguraition(“DEBUG”);



AssemblyCopyrightAttribute  Specifica informatia de copyright  [assebly: AssemblyCopyright(“ViPrAd HOME”)];



AssemblyCultureAttribute  Specifica numele culturii assembly-ului  [assebly: AssemblyCulture(“de”)];



AssemblyDefaultAliasAttribute  Este folosit pentru a simplifica numele assembly-ului. Poate fi folosit atunci cand numele assemblyului este lung si complicat. De ex daca assembly-ul se numeste Proposeware.Framework.Foundation.DataLayer puteti folosii doar DataLayer.  [assembly: AssemblyDefaultAlias(“DataLayer”)];



AssemblyDelaySignAttribute  Este folosit pentru a specifica faptul ca assembly-ul va fi semna dupa compilare (va primii un strong name). Daca este specificat acest atribut trebuie specificat si AssemblyKeyFileAttribute in care sa specificati cheia temporara folosita pentru semnare.  [assembly: AssemblyDelaySign(true)]



AssemblyDescriptionAttribute  Contine un string ce descrie assemby-ul.  [assemby: AssemblyDescription(“Descriere assembly”)]



AssemblyFileVersionAttribute  Contine versiunea assemby-ului. Aceasta versiune e vizibila sistemului de fisiere.  [assembly: AssemblyFileVersion(“1.0.0.0”)];



AssemblyInformationalVersionAttribute  Aceasta versiune are doar scop informational. Nu este folosita de runtime  [assembly: AssemblyInformationalVersion (“1.0.0.1”)];



AssemblyFlagsAttribute  [assembly: AssemblyFlags(AssemblyNameFlags.EnableJITCompileOptimizer | AssemblyJITCompileTracking)]



AssemblyKeyFileAttribute  Specifica o cale spre o cheie cu care va fi semnata assembly-ul. Utilizatorul sn.exe poate fi folosit pentru generarea unor fisiere cheie.  [assembly: AssemblyKeyFile(“cheie.snk”)]



AssemblyTitleAttribute  Numele assemby-ului (care apare in manifest)  [assembly: AssemblyTitle(“Proposeware.Framework.Datalayer”)]



AssemblyTrademarkAttribute  Informatii despre trademark-ul asociat assemby-ului.  [assembly: AssemblyTrademark(“Proposeware”)]



AssemblyVersionAttribute  Folosit pentru a specifica versiunea assemby-ului.  Are formatul ...  Permite inlocuirea build number-ului si a revisiei cu *. Acestea vor fi apoi automat actualizate de runtime la compilare. Revisia va fi generata aleator. Daca specificati numarul revisiei nu puteti pune * la build number.  [assembly: AssemblyVersion(“1.2.*.*”)]





 

Puteti folosii clasa Assembly si metoda GetCustomAttributes pentru a citi care sunt atributele unui assembly. Aceasta metoda este parte a interfetei ICustomAttributeProvider. ICustomAttributeProvider este mostenita de toate atributele specificate anterior. Prin parametrul bool de la GetCustomAttributes specificam daca dorim numai atributele definite direct (deci nu prin mostenire)

 



Clasa Type reprezinca un singur tip. O putem folosii pentru a ne uita la metode, proprietati, event-uri interfete si la arborele mostenirii. Are multi  membrii. Vedeti paginile 854 – 856 in carte.



Putem obtine obiect Type in urmatoarele moduri:  Din clasa Assembly  Assembly a = Assembly.GetExecutingAssembly();  Type[] assemblyTypes = a.GetTypes();

 Din clasa Module  Module[] mods = a.GetModules();  Type[] moduleTypes = mods[0].GetTypes();

 Din instanta unor obiecte  object o = new object();  Type objectType = o.GetType();

 Folosind cuvantul cheie typeof.  Type specificType = typeof(Int32);





Odata ce avem o clasa Type pentru un tip putem extrage informatii despre tip

Putem obtine informatii despre atributele tipurilor



   

Un tip este format din metode, proprietati, campuri si evenimente. Fiecare astfel de componenta este reprezentata in reflexie de o clasa proprie, care se termina cu Info. Ex: MethodInfo. Toate aceste clase deriva din MemberInfo. Chiar clasa Type deriva din MemberInfo. Clasa Type contime metode Get*** cu care puteti obtine clase de tip MethodInfo, EventInfo, etc. Exemplu: Foreach (PropertyInfo prop in t.GetProperties()) { // foloseste prop }

 



Clasa Type contine si o metoda care intoarce tipurile din interiorul altor tipuri (Nested Types). Ex: foreach (Type nestedType in t.GetNestedTypes()) { //use the nestedType } Puteti itera prin tot membrii unei clase (indiferent de tip) folosind metoda GetMembers. Foreach (MemberInfo member in t.GetMembers()) { //use the member }



Clasele care deriva MemberInfo sunt:      

ConstructorInfo EventInfo FieldInfo LocalVariableInfo MethodInfo MethodBase: Reprezinta orice membru care contine cod. Este clasa de baza pentru ContructorInfo si MethodInfo  PropertyInfo  Type 

Puteti verifica tipul unui MemberInfo folosind enumerarea MemberTypes:  If (member.MemberType == MemberTypes.PropertyInfo) // e o proprietate



 

Codul din interiorul unul membru poate fi citit prin intermediul MemberBody si a metodei GetMemberBody din MethodInfo sau ConstructorInfo. MethodBody body = metoda.GetMethodBody(); Din MethodBody puteti extrage informatii despre variabilele local precum si codul IL in bytes.

 

Folosind enumerarea BindingFlags puteti controla ce fel de membrii (ex: public, private) preluati cu GetMember. Puteti folosii mai multe valori a BindingFlags ca parametru la GetMember (veti face un sau logic intre elemente ale enumerarii).







Reflexia va permite sa scrieti cod in mod dinamic. Puteti rula astfel cod in assembly-uri, chiar daca nu le-ati referentiat inainte. Sa prespunem urmatorul cod: Hashtable tbl = new Hashtable(); tbl.Add(“Hi”,”Hello”); Console.WriteLine(“Hash count: {0}”, tbl.Count); Vom scrie codul de mai sus facand apeluri la reflexie.



 



1> Incarcam assembly-ul care contine clasa Hash in o clasa Assembly string path = @”C:\Windows\Microsoft.NET\Framework\\”+”mscorelib.d ll”; Assembly assembly= Assembly.LoadFile(path); 2> Incarcam tipul hash in o clasa Type Type hashType = assembly.GetType(“System.Collections.HashTable”); 3> Incarcam constructorul in un obiect ConstructorInfo Type[] argumentTypes = Type.EmptyTypes; // pentru constructor fara parametrii ConstructorInfo ctor = hashType.GetConstructor(argumnentTypes); 4> apelam constructorul si va rezulta o instanta a unui hash object newHash = ctor.Invoke(new object[]{});







1> alegem metoda pe care o vom invoca, din tipul creat adineauri MethodInfo metoda = hashType.GetMethod(“Add”); 2> invocam metoda metoda.Invoke(newHash, new object[] {“Hi”,”Hello”}; Pentru invocarea proprietatilor intai construim o clasa PropertyInfo PropertyInfo prop = hashType.GetProperty(“Count”);



Apelam proprietatea GetValue Int nr = (int) prop.GetValue(newHash, null);



Singura diferenta intre invocarea metodelor statice si a celor normale este ca nu trebuie instantiat tipul. Type consoleType = typeof(Console); MethodInfo writeLineMethod = consoleType.GetMethod(“WriteLine”, new Type[] {typeof(string)}); writeLineMethod.Invoke(null, new object[] {count.ToString()});









Sistemul de reflexie are un namespace numit Emit care contine mai multe clase ce pot fi folosite la constructia de assembly-uri, tipuri, metode, etc. Atunci cand creati cod la runtime trebuie incapsulat ca orice alt cod: construiti assembly-ul, un modul in assembly si apoi tipurile din modul. Pentru fiecare astfel de operatie exista o clasa didecata: AssemblyBuilder deriva din Assembly, MethodBuilder din MethodInfo, TypeBuilder din Type. Exista si: ConstructorBuilder, EnumBuilder, EventBuilder, FieldBuilder, LocalBuilder, ModuleBuilder, ParameterBuilder, PropertyBuilder, TypeBuilder.

  

Inainte de a scrie cod trebuie creat un assembly si un modul il el. Vom cere clasei AppDomain sa creeze assemby-ul. Aceasta primeste ca parametru un obiect AssemblyName si un element al enumararii AssemblyBuilderAccess. {ReflectionOnly, Run, Save, RunAndSave);  AssemblyName tempName = new AssemblyName();  tempName.Name = “MyTempAssembly”;  AssemblyBuilder assembBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(tempName, AssemblyBuilderAccess.RunAndSave);  ModuleBuilder modBuilder = new assembBuilder.DefineDynamicModule(“MainMod”,”MyTempAssembl y.dll”);



Vom folosii metoda DefineType apelata in noul ModuleBuilder creat:  TypeBuilder typeBuilder = modBuilder.DefineType(“MyNewType”, TypeAttributes.Public | TypeAttributes.Class);



Putem specifica si interfete sau clase pe care tipul sa le mosteneasca:  TypeBuilder typeBuilder = modBuilder.DefineType(“MyNewType”, TypeAttributes.Public | TypeAttributes.Class, typeof(Hashtable), new Type {typeof(Idisposible)});



Intai trebuie creat un constructor:  ConstructorBuilder ctorBuilder = new typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

 

Urmeaza scrierea de cod IL in constructor. Se poate folosii clasa ILGenerator.  IlGenerator codGen = new ctorBuilder.GetILGenerator();  codGen.Emit(OpCodes.Ret);



Urmeaza crearea de metode:  MethodBuilder methodBuilder = typeBuilder.DefineMethod(“Add”,MethodAttributes.Public, null, new Type[] {typeof(string)});  Putem adauga si alte atribute : ex: MethodAttreibutes.Static



Urmeaza crearea unui camp:  FieldBuider fieldBuilder = typeBuilder.DefineField(“_count”, typeof(int), FieldAttributes.Private);



Urmeaza crearea unei proprietati pentru accesarea campului:  PropertyBuilder probBuilder = typeBuilder.DefineProperty(“Count”, PropertyAttributes.None, typeof(int), Type.EmptyTypes);  Trebuie create si metode get / set care sa trateze apelul proprietatii. Atributele proprietatii le setati din method builder.  MethodBuider propGetBuilder = typeBuilder.DefineMethod(“get_Count”, MethodAttribute.Public, typeof(int), Type.EmptyTypes);  Trebuie legate metoda de proprietate:  probBuilder.SetGetMethod(probGetBuilder);



In final stocam assembly-ul pe disk: assembBuilder.Save(“MyTempAssembly.dll”





1. Realizati o aplicatie care sa scrie intr-un fisier toate detaliile dintr-o clasa din .Net. 2. Creati un dll in care sa existe o clasa ce realizeaza toate operatiile (+,-,*,/). Incarcati dll-ul la runtime, apelati metodele clasei si afisati rezultatele.

Related Documents

Lab 11 - Reflection
May 2020 8
Lab 11
November 2019 18
Lab 11
June 2020 13
Thai Reflection 11
May 2020 6
Reflection
April 2020 18
Reflection
November 2019 25