Dynamické volání metod v X++

X++ je staticky typovaný jazyk, že? Nicméně občas ho potřebujeme použít dynamicky…

Nejčastější případ je, že potřebujete volat metody definované na formuláři. To nelze udělat se statickými typy – formuláře jsou technicky instance třídy SysSetupFormRun, která očividně nezahrnuje metody, které jste přidali do svého formuláře. Také nemůžete referenci přetypovat na typ formuláře, protože formuláře nejsou typy v X++. Z pohledu AX formuláře nemají metody přidané při vytváření; metody jsou přidány dynamicky do instance SysSetupFormRun na základě definice formuláře. Takže je musíme také volat dynamicky.

Řešením je přetypovat referenci na typ Object. Možná byste očekávali, že vám kompilátor X++ povolí volat pouze metody definované ve třídě Object, ale není tomu tak. Kompilátor ve skutečnosti nekontroluje žádné metody volané na referenci typu Object a AX je za běhu zkusí zavolat. Toto je platný, zkompilovatelný X++ kód, který pochopitelně selže:

Object o = new Object();
// vyhazuje výjimku "Object object does not have method 'neexistujiciMetoda'",
// tzn. "Objekt object nemá metodu 'neexistujiciMetoda'".
o.neexistujiciMetoda();

Pro porovnání, ten samý kód by nešel zkompilovat v C#, protože C# používá i v tomto případě statické typování. Podobné chování byste dostali za pomoci typu dynamic:

dynamic o = new Object();
// vyhazuje výjimku RuntimeBinderException "'object' does not contain a definition for 'neexistujiciMetoda'",
// tzn. "'object' neobsahuje definici pro 'neexistujiciMetoda'".
o.neexistujiciMetoda();

Stejný přístup můžete použít pro volání formulářových metod. Například chcete zavolat metodu refreshEverything() na volajícím formuláři:

Object callerForm = element.caller();
callerForm.refreshEverything();

Tohle funguje a občas se to hodí. Ale musíte si být vědomi důsledků. Protože tam není žádná kontrola během kompilace, nedostanete žádné kompilační chyby, pokud metoda neexistuje (třeba protože ji někdo přejmenoval). Dynamická volání také nejsou zahrnuta v křížových referencích, protože kompilátor zkrátka neví, co voláte. To má značně negativní vliv na udržovatelnost vašeho kódu. V případě formulářů můžete zkontrolovat, zda má nějaký formulář konkrétní metodu pomocí formHasMethod(), ale to je stále jen kontrola za běhu.

Tyto problémy jsou závažné, proto byste se měli dynamickému typování vyhnout kdekoli je to možné. Například místo volání metod na formulářích můžete předat instanci třídy jako caller nebo parmObject, přetypovat ji na patřičnou třídu nebo rozhraní a volat metody typově bezpečným způsobem.