Hlavní navigace

Nerozumím...

20. 11. 2011 23:26 | zboj

Významnou vlastností některých dynamických jazyků je schopnost flexibilně reagovat na volání metody, která u daného objektu neexistuje. Jak známo, Smalltalk má zprávu doesNotUnderstand, kterou objekt dostane, pokud neumí obsloužit jinou zprávu. Předaný objekt aMessage rozumí zprávám selector (vrací onu neznámou zprávu) a arguments (argumenty volání). Tímto způsobem je možné implementovat jakousi záchrannou síť, která se aktivuje v případě zaslání zprávy neznámé v době překladu.

Narozdíl od staticky typovaných jazyků s prostou introspekcí (Java a C# před verzí 4) je tak možné implementovat dynamicky rozšiřitelné objekty. Objective-C mělo kdysi stejný systém jako Smalltalk, dnešní verze má methodSignatureForSelector a forwardInvocation, což je prakticky to samé v bledě modrém. Neznámá zpráva je zabalena do NSInvocation a předána forwardInvocation. Standardní implementace jednoduše vyhodí výjimku („selector not recognized“), ve speciálních případech lze ale zprávu přesměrovat nebo jinak ošetřit a výjimce se tak vyhnout. Pro některé speciální případy má nová verze runtimu ještě forwardingTargetForSelector, umožňující zprávu přesměrovat bez vysoké režie forwardInvocation a NSInvocation.

V dávných dobách OpenStepu byly na této technice založené distribuované objekty (něco jako pozdější DCOM, ale „light weight“). Své doesNotUnderstand“ dostalo i C#. Ve čtvrté verzi je možné u podtříd DynamicObject předefinovat metodu TryInvokeMember (a některé další, např. pro přístup k vlastnostem). Tak lze implementovat plně dynamický objekt, jak jej známe například z Javascriptu. S použitím dynamic (něco jako var, ale během překladu nedochází k odvození typu) můžeme deklarovat dynamický objekt (např. dynamic obj) a pak použít kdekoliv v kódu obj.Neco(), i když žádná taková metoda neexistuje (překladač si nebude stěžovat a za běhu se zavolá TryInvokeMember).

Je zřejmé, že takovéto odchytávání zpráv (resp. volání metod v C#) s sebou nese značnou režii a většinou není potřebné. Typickým použitím jsou právě distribuované objekty, kde režie navíc nevadí (následná síťová komunikace je řádově horší) a vyhneme se elegantně stubům a skeletonům (pokud nevíte, o čem mluvím, přečtěte si něco o RMI v Javě).