Metadatan kyseleminen Reflection-metodeilla



Samankaltaiset tiedostot
Attribuutit. Copyright IT Press Tämän e-kirjan kopiointi, tulostaminen ja jakeleminen eteenpäin luvatta on kielletty.

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Olio-ohjelmointi Javalla

Koosteet. Johdanto koosteisiin

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Delegaatit ja tapahtumakäsittelijät

Ohjelmoinnin jatkokurssi, kurssikoe

5. HelloWorld-ohjelma 5.1

Rajapinta (interface)

4. Luokan testaus ja käyttö olion kautta 4.1

C#-luokkien perusteet

5. HelloWorld-ohjelma 5.1

Mikä yhteyssuhde on?

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Jypelin käyttöohjeet» Ruutukentän luominen

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

Ohjelmointikielet ja -paradigmat 5op. Markus Norrena

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Oliot viestivät metodeja kutsuen

Operaattorin ylikuormitus ja käyttäjän muunnokset

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

Apuja ohjelmointiin» Yleisiä virheitä

C# olio-ohjelmointi perusopas

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Ohjelmointi 2 / 2010 Välikoe / 26.3

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

.NET ajoympäristö. Juha Järvensivu 2007

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Pedacode Pikaopas. Java-kehitysympäristön pystyttäminen

Taulukot. Jukka Harju, Jukka Juslin

Metodien tekeminen Javalla

Ohjelmoinnin perusteet Y Python

12 Mallit (Templates)

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Harjoitustyö: virtuaalikone

Luokat ja oliot. Ville Sundberg

1. Omat operaatiot 1.1

Java-kielen perusteet

9. Periytyminen Javassa 9.1

Sisällys. JAVA-OHJELMOINTI Osa 6: Periytyminen ja näkyvyys. Luokkahierarkia. Periytyminen (inheritance)

Ohjelmistotekniikan menetelmät, koe

Javan perusteita. Janne Käki

Pong-peli, vaihe Aliohjelman tekeminen. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 3/7. Tämän vaiheen aikana

Ohjelmointi 1 C#, kevät 2013, 2. tentti

Sisällys. 15. Lohkot. Lohkot. Lohkot

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

812341A Olio-ohjelmointi, IX Olioiden välisistä yhteyksistä

ITKP102 Ohjelmointi 1 (6 op)

1. Kun käyttäjä antaa nollan, niin ei tulosteta enää tuloa 2. Hyväksy käyttäjältä luku vain joltain tietyltä väliltä (esim tai )

Luokka Murtoluku uudelleen. Kirjoitetaan luokka Murtoluku uudelleen niin, että murtolukujen sieventäminen on mahdollista.

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

1 Tehtävän kuvaus ja analysointi

9. Periytyminen Javassa 9.1

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Osio 4: Tietovirrat. Properties- eli ominaisuustiedostot Logger: lokitietojen käsittely

15. Ohjelmoinnin tekniikkaa 15.1

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Pakkaukset ja määreet

public static void main (String [] args)

Java-API, rajapinnat, poikkeukset, UML,...

Ohjelmointi 1. Kumppanit

Osoitin ja viittaus C++:ssa

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

on ohjelmoijan itse tekemä tietotyyppi, joka kuvaa käsitettä

Ohjelmoinnin perusteet, syksy 2006

Java kahdessa tunnissa. Jyry Suvilehto

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

7. Näytölle tulostaminen 7.1

8. Näppäimistöltä lukeminen 8.1

15. Ohjelmoinnin tekniikkaa 15.1

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

Toisessa viikkoharjoituksessa on tavoitteena tutustua JUnit:lla testaukseen Eclipse-ympäristössä.

Sisältö. 22. Taulukot. Yleistä. Yleistä

A) on käytännöllinen ohjelmointitekniikka. = laajennetaan aikaisemmin tehtyjä luokkia (uudelleenkäytettävyys)

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

8. Näppäimistöltä lukeminen 8.1

7. Oliot ja viitteet 7.1

ITKP102 Ohjelmointi 1 (6 op)

ITKP102 Ohjelmointi 1 (6 op)

Hello, C# Ensimmäisen C#-sovelluksen kirjoittaminen. Editorin valinta

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

C++11 Syntaksi. Jari-Pekka Voutilainen Jari-Pekka Voutilainen: C++11 Syntaksi

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Ohjelmoinnin perusteet Y Python

1. Miten tehdään peliin toinen maila?

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

812347A Olio-ohjelmointi, X Reflektiivisyys

YHTEYSSUHDE (assosiation)

Listarakenne (ArrayList-luokka)

Sisältö. 2. Taulukot. Yleistä. Yleistä

JavaRMI 1 JAVA RMI. Rinnakkaisohjelmoinnin projekti 1 osa C Tekijät: Taru Itäpelto-Hu Jaakko Nissi Mikko Ikävalko

Tietokannat II -kurssin harjoitustyö

Collector for ArcGIS. Ohje /

DXL Library ja DXL-kielen olemus. Pekka Mäkinen SoftQA Oy http/

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Virtuaalifunktiot ja polymorfismi

Transkriptio:

16 Metadatan kyseleminen Reflection-metodeilla Luvussa 2, Johdanto Microsoft.NETiin, kerroin, miten kääntäjä generoi Win32 Portable Executable (PE)-tiedoston, joka sisältää MSIL:n ja metadatan. Yksi.NETin tehokas ominaisuus on se, että voit kirjoittaa koodia, joka käsittelee sovelluksen metadataa reflectionnimisen toiminnon avulla. Yksinkertaisesti sanoen, reflection on kyky selvittää tyypin informaatio suorituksen aikana. Tässä luvussa käsittelen reflection-api:n ja miten käyt sen avulla läpi koosteen moduulit ja tyypit ja haet erilaisia tyypin suunnitteluaikaisia ominaisuuksia. Näet myös muutamia kehittyneempiä reflection-menetelmän käyttätapoja, kuten metodien dynaamisen käynnistämisen ja tyyppi-informaation käyttämisen myöhäisessä sidonnassa ja jopa MSIL-koodin luomisen ja suorittamisen suorituksen aikana! Reflection-API:n rakenne.net Reflection API on joukko System.Reflection-nimiavaruudessa määriteltyjä luokkia. Osan luokista näet kuvassa 16-1. Näiden luokkien avulla voit selvittää koosteen ja tyypin tietoja. Voit aloittaa mistä tahansa hierarkkian kohtaa sovelluksesi tarpeet vaativat. 327

Osa IV Vaativampi C# System.Reflection Assembly Module MethodInfo FieldInfo PropertyInfo Type-luokka EventInfo Kuva 16-1 Osa.NETin System.Reflection-luokkahierarkkiaa. Nämä luokat käsittävät suurimman osan System.Reflection -luokkien toiminnallisuudesta. En aio luetella jokaisen luokan jokaista metodia ja kenttää, vaan esitän tärkeimpien luokkien perusteet ja näytän esimerkkiohjelman, joka sisältää ne toiminnot, joita luultavimmin tulet omissa sovelluksissasi tarvitsemaan. Koko toiminnon ytimenä on System.Type-luokka. Se on abstrakti luokka, joka esittää Common Type System (CTS):n tyyppiä. Sen avulla voit kysyä tyypin nimen, tyypin sisältämät modulit ja nimiavaruudet ja sen, onko tyyppi arvotyyppi vain viittaustyyppi. Instanssin tyypin selvittäminen Seuraava esimerkki näyttää, miten haet instantioidun int-tyypin Type-objektin: using System.Reflection; class TypeObjectFromInstanceApp public static void Main(string[] args) int i = 6; Type t = i.gettype(); Console.WriteLine(t.Name); 328

Metadatan kyseleminen Reflection-metodeilla Luku 16 Type-objektin hakeminen nimen perusteella Sen lisäksi, että voit hakea muuttujan Type-objektin, voit myös luoda Type-objektin tyypin nimen avulla. Toisin sanoen, sinulla ei tarvitse olla tyypin instanssia. Tässä esimerkki, joka tekee tämän System.Int32-tyypille: using System.Reflection; class TypeObjectFromNameApp public static void Main(string[] args) Type t = Type.GetType( System.Int32 ); Console.WriteLine(t.Name); Huomaa, että et voi käyttää C#:n peitenimiä, kun kutsut Type.GetType-metodia, koska sitä käytetään kaikissa kielissä Siksi et voi käyttää C#:n int-peitenimeä System.Int32:n sijalla. Tyyppien tulkitseminen Voit System.Type-luokan avulla myös kysyä tyypiltä sen lähes kaikkia attribuutteja, näiden käsittelymääreitä ja ovatko ne sisäkkäisiä, sen COM-ominaisuuksia ja niin edelleen. Seuraava koodi näyttää tämän. Käytän siinä useita tavallisia tyyppejä sekä muutamia omia tyyppejä: using System.Reflection; interface DemoInterface class DemoAttr : System.Attribute enum DemoEnum public class DemoBaseClass (jatkuu) 329

Osa IV Vaativampi C# public class DemoDerivedClass : DemoBaseClass class DemoStruct class QueryTypesApp public static void QueryType(string typename) try Type type = Type.GetType(typeName); Console.WriteLine( Type name: 0", type.fullname); Console.WriteLine( \thaselementtype = 0", type.haselementtype); Console.WriteLine( \tisabstract = 0", type.isabstract); Console.WriteLine( \tisansiclass = 0", type.isansiclass); Console.WriteLine( \tisarray = 0", type.isarray); Console.WriteLine( \tisautoclass = 0", type.isautoclass); Console.WriteLine( \tisautolayout = 0", type.isautolayout); Console.WriteLine( \tisbyref = 0", type.isbyref); Console.WriteLine( \tisclass = 0", type.isclass); Console.WriteLine( \tiscomobject = 0", type.iscomobject); Console.WriteLine( \tiscontextful = 0", type.iscontextful); Console.WriteLine( \tisenum = 0", type.isenum); Console.WriteLine( \tisexplicitlayout = 0", type.isexplicitlayout); Console.WriteLine( \tisimport = 0", type.isimport); Console.WriteLine( \tisinterface = 0", type.isinterface); Console.WriteLine( \tislayoutsequential = 0", type.islayoutsequential); Console.WriteLine( \tismarshalbyref = 0", type.ismarshalbyref); Console.WriteLine( \tisnestedassembly = 0", type.isnestedassembly); Console.WriteLine( \tisnestedfamandassem = 0", type.isnestedfamandassem); Console.WriteLine( \tisnestedfamily = 0", type.isnestedfamily); Console.WriteLine( \tisnestedfamorassem = 0", 330

Metadatan kyseleminen Reflection-metodeilla Luku 16 type.isnestedfamorassem); Console.WriteLine( \tisnestedprivate = 0", type.isnestedprivate); Console.WriteLine( \tisnestedpublic = 0", type.isnestedpublic); Console.WriteLine( \tisnotpublic = 0", type.isnotpublic); Console.WriteLine( \tispointer = 0", type.ispointer); Console.WriteLine( \tisprimitive = 0", type.isprimitive); Console.WriteLine( \tispublic = 0", type.ispublic); Console.WriteLine( \tissealed = 0", type.issealed); Console.WriteLine( \tisserializable = 0", type.isserializable); Console.WriteLine( \tisservicedcomponent = 0", type.isservicedcomponent); Console.WriteLine( \tisspecialname = 0", type.isspecialname); Console.WriteLine( \tisunicodeclass = 0", type.isunicodeclass); Console.WriteLine( \tisvaluetype = 0", type.isvaluetype); catch(system.nullreferenceexception) Console.WriteLine ( 0 is not a valid type", typename); public static void Main(string[] args) QueryType( System.Int32 ); QueryType( System.Int64 ); QueryType( System.Type ); QueryType( DemoAttr ); QueryType( DemoEnum ); QueryType( DemoBaseClass ); QueryType( DemoDerivedClass ); QueryType( DemoStruct ); 331

Osa IV Vaativampi C# Työskentely koosteilla ja moduleilla Koosteista puhutaan tarkemmin luvussa 18, Työskentely koosteilla. Tämän luvun asiaa varten kerron, että kooste on fyysinen tiedosto, joka sisältää useita.net PE -tiedostoja. Koosteen suurin etu on siinä, että voit sen avulla koota yhteen joukon toimintoja helpottamaan jakelua ja versiointia..netin koosteen ajonaikainen ympäristö (ja reflectionluokkahierarkkian kantaluokka) on Assembly-luokka. Assembly-luokalla voit tehdä monia asioita. Seuraavana on muutamia yleisempiä tehtäviä, joita katsomme kohta tarkemmin: Koosteen tyyppien selvittäminen Luettelo koosteen moduleista Tunnistusinformaation (koosteen fyysinen nimi ja sijainti) määrittäminen Versiointi ja turva-informaation tulkinta Koosteen aloituspisteen hakeminen Koosteen tyyppien selvittäminen Määrätyn koosteen sisältämien tyyppien selvittäminen onnistuu siten, että instantioit Assembly-objektin ja pyydät haluamasi koosteen Types-taulukkoa. Tässä esimerkki: using System.Diagnostics; using System.Reflection; class DemoAttr : System.Attribute enum DemoEnum class DemoBaseClass 332

Metadatan kyseleminen Reflection-metodeilla Luku 16 class DemoDerivedClass : DemoBaseClass class DemoStruct class GetTypesApp protected static string GetAssemblyName(string[] args) string assemblyname; if (0 == args.length) Process p = Process.GetCurrentProcess(); assemblyname = p.processname +.exe"; else assemblyname = args[0]; return assemblyname; public static void Main(string[] args) string assemblyname = GetAssemblyName(args); Console.WriteLine( Loading info for + assemblyname); Assembly a = Assembly.LoadFrom(assemblyName); Type[] types = a.gettypes(); foreach(type t in types) Console.WriteLine( \ntype information for: + t.fullname); Console.WriteLine( \tbase class = + t.basetype.fullname); 333

Osa IV Vaativampi C# Huomaa Jos yrität suorittaa intranetin kautta koodia, joka tarvitsee turvaselvityksen (kuten reflection-apia käyttävä koodi), sinun pitää muokata järjestelmän asetustiedostoa. Yksi tapa tehdä se on käyttää Code Access Security Policy (caspol.exe) -apuohjelmaa. Tässä esimerkki sen käytöstä: caspol -addgroup 1.2 -url file://somecomputer/someshare/ *" SkipVerification Tämä esimerkki myöntää lisäoikeuden (tässä tapauksessa SkipVerification-oikeuden) sen URL:n perusteella, josta koodi suoritetaan. Voit myös muokata kerralla kaikkia saman vyöhykkeen koodien oikeuksia tai vaikkapa vain yhden koosteen oikeuksia. Näet caspol.exe:n kaikki käyttövaihtoehdot kirjoittamalla käskyriville caspol -? tai tutkimalla MSDN-online-dokumentteja. Main-metodin alku ei ole erityisen mielenkiintoinen. Siinä määritellään, oletko syöttänyt sovellukselle koosteen nimen. Jos et, käytetään Process-luokan staattista GetProcessName-metodia hakemaan käynnissä olevan sovelluksen nimi. Sen jälkeen huomaat, miten helppoa useimmat reflection-tehtävät ovat. Helpoin tapa instantioida Assembly-objekti on kutsua Assembly.LoadFrom-metodia. Se ottaa yhden parametrin: merkkijonon, joka kertoo sen fyysisen tiedoton nimen, jonka haluat ladata. Sen jälkeen Assembly.GetTypes-metodin kutsu palauttaa taulukon Type-objekteja. Tässä vaiheessa meillä on objekti, joka kuvaa koko koosteen jokaisen yksittäisen tyypin. Lopuksi sovellus tulostaa kantaluokkansa. Tässä tuloste sovelluksen suorittamisesta, kun joko määrittelit gettypes.exe-tiedoston nimen tai et välittänyt sovellukselle lainkaan parametria: Loading info for GetTypes.exe Type information for: DemoAttr Base class = System.Attribute Type information for: DemoEnum Base class = System.Enum Type information for: DemoBaseClass Base class = System.Object 334

Metadatan kyseleminen Reflection-metodeilla Luku 16 Type information for: DemoDerivedClass Base class = DemoBaseClass Type information for: DemoStruct Base class = System.Object Type information for: AssemblyGetTypesApp Base class = System.Object Koosteen modulien luettelo Vaikka useimmat tämän kirjan sovelluksista koostuvat yhdestä modulista, voit luoda myös koosteita, jotka sisältävät useita moduleja. Voit hakea koosteen modulit kahdella tavalla. Ensimmäinen on pyytää modulit sisältävä taulukko. Sen avulla voit käydä ne kaikki läpi ja hakea tarvitsemasi tiedot. Toinen tapa on hakea määrätty moduli. Katsotaan kumpaakin vaihtoehtoa. Jotta voisin käydä koosteen modulit läpi, minulla pitää olla kooste, jossa on enemmän kuin yksi moduuli. Teen sellaisen siirtämällä GetAssemblyName-metodin omaan luokkaansa ja sijoittamalla sen AssemblyUtil.netmodule-nimiseen tiedostoon, tällä tavalla: using System.Diagnostics; namespace MyUtilities public class AssemblyUtils public static string GetAssemblyName(string[] args) string assemblyname; if (0 == args.length) Process p = Process.GetCurrentProcess(); assemblyname = p.processname +.exe"; else assemblyname = args[0]; return assemblyname; 335

Osa IV Vaativampi C# Moduli luodaan sitten seuraavasti: csc /target:module AssemblyUtils.cs /target:module-asetus määrää kääntäjän tekemään modulin, joka myöhemmin sijoitetaan koosteeseen. Yllä oleva käskyrivi luo AssemblyUtil.netmodule-nimisen tiedoston. Luvussa 18 kerron yksityiskohtaisesti eri asetuksista koosteiden ja modulien luonnissa. Tässä vaiheessa tarvitsen vielä toisen modulin, jotta meillä on jotain tutkittavaa. Seuraavassa sovellus, joka käyttää AssemblyUtils-luokkaa. Huomaa using-määreen käyttö, kun viitataan MyUtilities-nimiavaruuteen. using System.Reflection; using MyUtilities; class GetModulesApp public static void Main(string[] args) string assemblyname = AssemblyUtils.GetAssemblyName(args); Console.WriteLine( Loading info for + assemblyname); Assembly a = Assembly.LoadFrom(assemblyName); Module[] modules = a.getmodules(); foreach(module m in modules) Console.WriteLine( Module: + m.name); Tämän sovelluksen kääntäminen ja AssemblyUtils.netmodule-modulin lisääminen samaan koosteeseen tapahtuu seuraavanlaisella käskyrivillä: csc /addmodule:assemblyutils.netmodule GetModules.cs Nyt sinulla on kooste, jossa on kaksi erilaista modulia. Näet sen, kun käynnistät sovelluksen. Tulos näyttää tältä: Loading info for GetModulesApp.exe Module: GetModulesApp.exe Module: AssemblyUtils.netmodule Kuten näet koodista, yksinkertaisesti instantioin Assembly-objektin ja kutsuin sen GetModules-metodia. Sitten käyn paluutaulukon läpi ja tulostan kunkin modulin nimen. 336

Metadatan kyseleminen Reflection-metodeilla Luku 16 Myöhäinen sidonta Reflection-menetelmän avulla Muutama vuosi sitten työskentelin IBM:n multimediayksikössä, sen IBM/World Book Multimedia Encyclopedia -tuotteen parissa. Haaste, jonka kohtasimme sovelluksessa, aiheutui siitä, että halusimme antaa käyttäjälle mahdollisuuden asentaa erilaisia tiedonsiirtoprotokollia World Book -palvelimille. Ratkaisun tuli olla dynaaminen, jotta käyttäjä voisi tilanteen mukaan lisätä ja poistaa eri protokollia (esimerkiksi TCP/IP, IGN, CompuServe ja niin edelleen) järjestelmästään. Sovelluksen piti toisaalta tietää, mitä protokollia oli käytettävissä, jotta hän pystyi valitsemaan niistä jonkun käyttöön. Päädyimme ratkaisuun, jossa loimme määrätyn tarkenteen omaavia DLL:iä ja asensimme ne sovelluksen kansioon. Kun käyttäjä halusi nähdä luettelon asennetuista protokollista, sovellus kutsui Win32:n LoadLibrary-funktiota ladatakseen kunkin DLL:n ja kutsui sitten GetProcAddress-funktiota hankkiakseen funktio-osoittimen haluttuun funktioon. Tämä on täydellinen esimerkki myöhäisestä sidonnasta tavallisessa Win32-ohjelmoinnissa, jossa kääntäjä ei käännöksen aikana tiedä näistä kutsuista mitään. Kuten näet seuraavassa esimerkissä, sama tehtävä voidaan.netissä toteuttaa käyttämällä Assembly-luokkaa, tyypin selvittämistä ja uutta Activator-nimistä luokkaa. Jatketaan eteenpäin ja luodaan abstrakti luokka nimeltä CommProtocol. Määrittelen luokan omassa DLL:ssään, jotta se voidaan jakaa useiden DLL:ien kanssa, jotka haluavat periytyä siitä. (Huomaa, että käskyrivin parametrit on upotettu koodin kommentteihin.) // CommProtocol.cs // Muodostettu seuraavalla käskyrivikomennolla: // csc /t:library commprotocol.cs public abstract class CommProtocol public static string DLLMask = CommProtocol*.dll"; public abstract void DisplayName(); Luon nyt kaksi erillistä DLL:ää, jotka kumpikin kuvaavat tiedonsiirtoprotokollaa ja sisältävät luokan, joka on periytetty abstraktista luokasta CommProtocol. Huomaa, että molemmat tarvitsevat käännöksessä viittauksen CommProtocol.dll:ään. Tässä IGN DLL: // CommProtocolIGN.cs // Muodostettu seuraavalla käskyrivikomennolla: // csc /t:library CommProtocolIGN.cs /r:commprotocol.dll public class CommProtocolIGN : CommProtocol public override void DisplayName() (jatkuu) 337

Osa IV Vaativampi C# Console.WriteLine( This is the IBM Global Network ); Ja tässä TCP/IP DLL: // CommProtocolTcpIp.cs // Muodostettu seuraavalla käskyrivikomennolla: // csc /t:library CommProtocolTcpIp.cs /r:commprotocol.dll public class CommProtocolTcpIp : CommProtocol public override void DisplayName() Console.WriteLine( This is the TCP/IP protocol ); Katsotaan nyt, miten helppoa on dynaamisesti ladata kooste, selvittää tyyppi, instantioida tuota tyyppiä oleva objekti ja kutsua yhtä sen metodia (Kirjan mukana tulevalla cd:llä on muuten BuildLateBinding.cmd-niminen komentotiedosto, joka muodostaa nämä tiedostot.) using System.Reflection; using System.IO; class LateBindingApp public static void Main() string[] filenames = Directory.GetFiles (Environment.CurrentDirectory, CommProtocol.DLLMask); foreach(string filename in filenames) Console.WriteLine( Loading DLL 0 ", filename); Assembly a = Assembly.LoadFrom(fileName); Type[] types = a.gettypes(); foreach(type t in types) if (t.issubclassof(typeof(commprotocol))) 338

Metadatan kyseleminen Reflection-metodeilla Luku 16 object o = Activator.CreateInstance(t); MethodInfo mi = t.getmethod( DisplayName ); Console.Write( \t ); mi.invoke(o, null); else Console.WriteLine( \tthis DLL does not have + CommProtocol-derived class defined ); Käytin ensin System.IO.Directory-luokkaa hakiessani kansiosta kaikki hakuehtoon CommProtocol*.dll täsmäävät DLL:t. Directory.GetFiles-metodi palauttaa taulukon, joka sisältää ehtoon sopivat tiedostonimet. Sen jälkeen voin käyttää foreach-silmukkaa taulukon läpikäyntiin ja kutsua jokaisen kohdalla jo aiemmin tässä luvussa käytettyä Assembly.LoadFrom-metodia. Kun kooste on ladattu annetusta DLL:stä, käyn läpi sen kaikki tyypit ja kutsun Type.SubClassOf-metodia määritelläkseni onko koosteessa tyyppiä, joka periytyy CommProtocol-luokasta. Oletan, että jos sellainen löytyy, niin DLL on kelvollinen. Kun löydän koosteen, joka sisältää CommProtocol-luokasta periytyvän tyypin, instantioin Activator-objektin ja välitän sen muodostimelle type-objektin. Kuten varmaan arvaatkin jo nimestä, Activator-luokkaa käytetään luomaan (tai aktivoimaan) tyyppi dynaamisesti. Sen jälkeen käytän Type.GetMethod-metodia MethodInfo-objektin luomiseen määritellen metodin nimeksi DisplayName. Kun olen tehnyt sen, voin käyttää MethodInfo-objektin Invokemetodia, välittäen sille parametrina aktivoidun tyypin ja - DLL:n DisplayName-metodia kusutaan! 339

Osa IV Vaativampi C# Koodin luominen ja ajaminen suorituksen aikana 340 Ny t kun olet nähnyt, miten selvität tyypin tietoja suorituksen aikana, teet myöhäisen sidonnan ja suoritat koodia dynaamisesti, otetaan seuraava looginen askel ja luodaan koodia lennossa. Tyyppien luominen suorituksen aikana tapahtuu käyttäen System.Reflection.Emitnimiavaruutta. Nimiavaruuden luokkien avulla voit määritellä koosteen, luoda modulin koosteeseen, määritellä uusia tyyppejä moduliin (mukaanlukien sen jäsenet) ja vieläpä lisätä MSIL-käskyjä sovelluksen logiikkaa varten. Vaikka tämän esimerkin koodi on äärimmäisen yksinkertainen, olen erottanut palvelinkoodin (DLL:n, joka sisältää luokan, joka luo HelloWorld-nimisen metodin) asiakaskoodista eli sovelluksesta, joka instantioi koodin generoivan luokan ja kutsuu sen HelloWorld-metodia. (Huomaa, että kääntäjän valitsimet on kerrottu kommenteissa.) Selitykset tulevat seuraavan DLL-koodin jälkeen: using System.Reflection; using System.Reflection.Emit; namespace ILGenServer public class CodeGenerator public CodeGenerator() // Haetaan currentdomain. currentdomain = AppDomain.CurrentDomain; // Luodaan kooste. assemblyname = new AssemblyName(); assemblyname.name = TempAssembly"; assemblybuilder = currentdomain.definedynamicassembly (assemblyname, AssemblyBuilderAccess.Run); // luodaan moduli koosteeseen modulebuilder = assemblybuilder.definedynamicmodule ( TempModule ); // luodaan tyyppi moduliin typebuilder = modulebuilder.definetype ( TempClass", TypeAttributes.Public); // lisätään jäsen (metodi) tyyppiin methodbuilder = typebuilder.definemethod ( HelloWorld", MethodAttributes.Public, null,null);

Metadatan kyseleminen Reflection-metodeilla Luku 16 // Generoidaan MSIL. msil = methodbuilder.getilgenerator(); msil.emitwriteline( Hello World ); msil.emit(opcodes.ret); // Viimeinen askel : luodaan tyyppi. t = typebuilder.createtype(); AppDomain currentdomain; AssemblyName assemblyname; AssemblyBuilder assemblybuilder; ModuleBuilder modulebuilder; TypeBuilder typebuilder; MethodBuilder methodbuilder; ILGenerator msil; object o; Type t; public Type T get return this.t; Aluksi instantioimme AppDomain-objektin sovelluksen domainissa. (Näet luvussa 17, Yhteistoiminta hallitsemattoman koodin kanssa, että sovelluksen domainit ovat samanlaisia kuin Win32:n prosessit.) Sen jälkeen instantioimme AssemblyName-objektin. AssemblyName-luokasta puhutaan yksityiskohtaisesti luvussa 18, mutta voin tässä sanoa, että se on luokka, jota koostevaraston ylläpito-ohjelma käyttää hakiessaan tietoja koosteesta. Kun meillä on sovelluksen domain ja alustettu koosteen nimi, luomme uuden koosteen AppDomain.DefineDynamicAssembly-metodilla. Huomaa, että sille välitettävät parametrit ovat koosteen nimi sekä muoto, jolla koostetta voidaan käsitellä. AssemblyBuilderAccess.Runmuoto tarkoittaa, että kooste voidaan suorittaa muistissa, mutta sitä ei voi tallentaa. AppDomain.DefineDynamicAssembly-metodi palauttaa AssemblyBuilder-objektin, jonka muunnamme Assembly-objektiksi. Tässä vaiheessa meillä on muistissa täysin toimiva kooste. Nyt meidän pitää luoda sen tilapäinen moduli ja moduliin tyyppi. Aloitamme kutsumalla Assembly.DefineDynamicModule-metodia saadaksemme ModuleBuilder-objektin. Kun meillä on se, kutsumme sen DefineType-metodia luodaksemme TypeBuilder-objektin, välittäen sille paramerina tyypin nimen ( TempClass ) ja attribuutit, 341

Osa IV Vaativampi C# jotka määrittelevät sen (TypeAttribute.Public). Nyt kun meillä on käsissämme TypeBuilderobjekti, voimme luoda minkä tyypin haluamme. Tässä tapauksessa luomme metodin käyttämällä TypeBuilder.DefineMethod-metodia. Lopulta meillä on uusi TempClass-niminen tyyppi, jossa on metodi nimeltä HelloWorld. Nyt meidän pitää päättää, mitä koodia tähän metodiin sijoitetaan. Koodi tehdään ILGenerator-objektilla käyttäen MethodBuilder.GetILGenerator-metodia ja kutsuen erilaisia ILGenerator-metodeja koodin kirjoittamiseen metodiin. Huomaa, että voimme tässä käyttää tavallista koodia, kuten Console.WriteLine, käyttämällä erilaisia ILGenerator-metodeja tai voimme tehdä MSIL-käskyjä käyttämällä ILGenerator.Emit-metodia. ILGenerator.Emit-metodi ottaa ainoana parametrinaan OpCodesluokan jäsenkentän, joka on liitetty suoraan MSIL-koodiin. Lopuksi kutsumme TypeBuilder.CreateType-metodia. Tämän tulee aina olla viimeinen suoritettava vaihe sen jälkeen, kun olet määritellyt uuden tyypin jäsenet. Sen jälkeen haemme uuden tyypin Type-objektin käyttämällä Type.GetType-metodia. Tämä objekti tallennetaan jäsenmuuttujaan, josta asiakasohjelma voi sen myöhemmin hakea. Nyt asiakasohjelman täytyy vain hakea CodeGenerator-luokan Type-jäsen, luoda Activator-luokan instanssi, instantioida tyypistä MethodInfo-objekti ja sen jälkeen käynnistää metodi. Tässä koodi joka tekee sen, lisättynä virhetarkistuksella, joka varmistaa, että kaikki toimii niin kuin pitää: using System.Reflection; using ILGenServer; public class ILGenClientApp public static void Main() Console.WriteLine( Calling DLL function to generate + "a new type and method in memory... ); CodeGenerator gen = new CodeGenerator(); Console.WriteLine( Retrieving dynamically generated type... ); Type t = gen.t; if (null!= t) Console.WriteLine( Instantiating the new type... ); object o = Activator.CreateInstance(t); 342

Metadatan kyseleminen Reflection-metodeilla Luku 16 Console.WriteLine( Retrieving the type s + "HelloWorld method... ); MethodInfo helloworld = t.getmethod( HelloWorld ); if (null!= helloworld) Console.WriteLine( Invoking our dynamically + "created HelloWorld method... ); helloworld.invoke(o, null); else Console.WriteLine( Could not locate + "HelloWorld method ); else Console.WriteLine( Could not access Type from server ); Jos nyt käännät ja käynnistät tämän sovelluksen, näet seuraavat tulokset: Calling DLL function to generate a new type and method in memory... Retrieving dynamically generated type... Instantiating the new type... Retrieving the type s HelloWorld method... Invoking our dynamically created HelloWorld method... Hello World Yhteenveto Reflection-menetelmällä voidaan selvittää tyypin tietoja suorituksen aikana. Reflection- APIn avulla voit esimerkiksi käydä läpi koosteen modulit ja koosteen tyypit ja hakea tyypin erilaisia ominaisuuksia. Kehittyneempiä reflection-menetelmän mahdollisuuksia ovat esimerkiksi mahdollisuus käynnistää metodeja dynaamisesti, käyttää tyyppejä (myöhäisellä sidonnalla) ja jopa luoda ja suorittaa MSIL-koodia suorituksen aikana. 343