Lubasin stringe pilduda
11.02.2010 | Gunnar
Ühele Eneta ettekandele järgnenud vabas vormis vestluses lubasin, et tutvustan natukene .NET raamistiku vahekeele (IL – Intermediate Language) ja CLR (Common Language Runtime) lõtva püksikummi. Näite lubasin teha stringide pildumise teemal ja siin see on.
Kellele IL võõras on, siis soovitan esmase kiire pildi saamiseks järgmisi kandeid:
Kui näited jäävad segaseks, siis aitab Google hädast välja. Igasugu abimaterjale, näiteid ja juhendeid leidub ohtralt.
Vigade pildumine
Alustuseks vaatame, kuidas toimub vigade pildumine vahekeeles.
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
newobj instance void [mscorlib]System.Exception::.ctor()
throw
}
}
Kui selle koodi kompileerime, siis käivitamisel saame kõrvaloleval pisikesel ekraanpaugul näidatud tulemuse. Kõik läks ootuspäraselt ja midagi veidrat veel ei juhtunud.
Järgmiseks vaatame, kas vahekeel ja selle assembler stringe samamoodi fännavad.
Stringide pildumine
Stringide pildumiseks peame eeltoodud koodi pisut muutma ja täiendama. Ma lisan juurde ühe uue catch-ploki, mis püüab Exception asemel kinni hoopis stringi. Et seda plokki testida, saadan ThrowSomething() meetodist teele ühe stringi. Kood selline.
.class private auto ansi Program extends [mscorlib]System.Object
{
.method public hidebysig static void Run() il managed
{
.entrypoint
.locals (class [mscorlib]System.Exception V_0)
.try
{
call void Program::ThrowSomething()
ldstr "Bye"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
catch [mscorlib]System.Exception
{
stloc.0
ldstr "Exception was thrown!"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
//
// CATCH STRING
//
catch System.String
{
stloc.0
ldstr "String was thrown"
call void [mscorlib]System.Console::WriteLine
(class System.String)
leave.s IL_001e
}
IL_001e: ldstr "Finish"
call void [mscorlib]System.Console::WriteLine
(class System.String)
ret
}
.method public hidebysig static void ThrowSomething()
il managed
{
//newobj instance void [mscorlib]System.Exception::.ctor()
//
// THROW STRING
//
ldsfld string [mscorlib]System.String::Empty
throw
}
}
Ma arvan, et enamus lugejaist ootab tulemuseks kas mõnda viga või hoiatust. Või vähemasti mingit jama kompileeritud koodi käivitamisel. Aga ei, vaadake paremal asuvat ekraanpauku, palun.
Kindlasti tekib küsimus, et kui vahekeeles on sellised asjad lubatud, siis miks kompilaatorid vigu annavad, kui midagi sellist tahaks teha? Catch-plokkide tüübid on kompilaatorite kontrolli all ja kompilaatorid meil midagi sellist teha ei luba. Kuivõrd IL meile takistusi ei tee, siis aitab CLR siin ehitatud eriolukorra tekkimisel kompilaatori poolt loodud koodi kenasti hädast välja.
Mängime Visual Studioga ka
Kui nüüd eeltoodud koodi täiendada nimeruumiga, kuhu meie ainum klass kuulub ja see siis DLL-iks kompileerida, saame seda kasutada ka Visual Studio projektides. Esimese katsena vaatame, kas C# koodi mõjutab see kuidagi, kui me õhu stringidest tiineks teeme, kuid jääme siiski nii palju viisakaks, et ise stringid kokku koristame. Kood on selline.
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.Run();
Console.ReadLine();
Console.ReadLine();
}
}
}
Selle koodi käivitamisel ei juhtu midagi põnevat. Saame umbes sama väljundi, mis viimasel juhul. Seega kui vahekeeles kirjutatud catch-plokk olukorraga toime tuleb, ei sega see ei C# kompilaatorit ega CLR-i.
Järgmiseks kutsume C# projektist ThrowSomething() meetodi. See tähendab seda, et string visatakse õhku, kuid enam seda miski kinni ei püüa. Kood järgmine.
namespace ConsoleExamples
{
class Program
{
static void Main(string[] args)
{
StringMess.Program.ThrowSomething();
Console.ReadLine();
}
}
}
Kui selle käivitame, siis antakse meile RuntimeWrappedException.
See on nüüd see koht, kus CLR appi tuleb. Kui tekib tõrge, mille baastüübiks pole Exception, siis CLR püüab selle tõrke ise kinni ning loob wrapperi, mille tüübiks on RuntimeWrappedException ja mille baasklassiks pärilushierarhias on ootuspärane Exception.
Siit jõuame tulemuseni, et vahekeeles on try-catch-finally plokk küll peaaegu sama, kuid puuduvad rangemad nõuded catch-ploki a throw tüüpidele.
Lõpetuseks
Nagu nägime, siis vahekeele tasemel pole nii palju piiranguid kui seda on konkreetsete .NET keelte tasemel tänu kompilaatoritele. Vahekeeles saame me tegelikult teha veel igasuguseid veidruseid ja osad neist on sellised, et .NET keeled neid asju näha ei oska. Anna kohe viite ka.
Kellel on vahekeele ja CLR-i sügavamate kõhusoppide, samuti .NET kompilaatorite ehituse vastu sügavam huvi, siis soovitan lugeda sellist raamatut nagu Expert .NET 2.0 IL Assembler. Raamatu autor Serge Lidin on antud teemal äärmiselt pädev tegija, sest tema on üks .NET 2.0 assembleri autoritest. Raamatust leiate nii mõndagi põnevat, samuti viite sellele, et arvutite peal tohib stringidega ka nii teha. Kes paberilt lugeda ei viitsi või soovib praegusel raskel ajal püsida säästurezhimil, siis on saadaval tasuta raamat vahekeele teemadel, mille autoriks on Vijay Mukhi ja millel pealkirjaks C# to IL.
Sellega on praeguseks minu poolt kõik. Kell on pisut üle südaöö ja paljude lugejate jaoks on vist käes see aeg, kui võib asuda päris stringide pildumise juurde. Need hõõguvate silmadega koodimaniakid, kellel too lugu hamba kergelt verele võttis, suunan lugema blogi kannet, kus istutatakse liidese külge staatiline meetod koos meetodi kehaga.
