Sunteți pe pagina 1din 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 <versiune majora>.<versiune minora>.<build number>.<revisie>
 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\<versiune>\”+”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.

S-ar putea să vă placă și