X++ compiler is very benevolent when dealing with types – much more than it should be. It happily accepts code that can’t succeed at run-time, as I’ll will demonstrate in a minute. It’s unfortunate, because some errors could have been detected at compile-time and they aren’t. And that’s not all – the X++ runtime is also more benevolent than CLR (Common Language Runtime), therefore the same code may run successfully as native AX and fail in CLR (after compiling X++ to CIL).
Imagine two X++ classes – a base class and a child class.
class BaseClass { public str name() { return "Base"; } } class ChildClass extends BaseClass { public str name() { return "Child"; } void childOperation() {} }
You can safely assign an instance of the child class to the parent class variable:
BaseClass base = new ChildClass();
This can’t ever go wrong – ChildClass extends BaseClass and it definitely has all its operations, because it inherits them. If you call base.name(), it will execute the implementation in ChildClass, therefore it will return “Child”. This is called polymorphism and it’s a very important component of object-oriented programming.
Now what if you try to do the opposite – to assign a variable of a general type to a specialized one? The C# compiler, for example, would throw an error (“Cannot implicitly convert ‘BaseClass’ to ‘ChildClass'”). The assignment will work only if base variable actually contains an instance of ChildClass. If it contains BaseClass or some other class extending from it, there could be a missing methods or other behavior, such as in the following example:
BaseClass base = new BaseClass(); ChildClass child = base; child.childOperation();
The child variable here contains an instance of BaseClass, which doesn’t have any childOperation(), therefore the code must fail. That’s why compilers usually don’t allow such code.
Nevertheless this is not the case of X++ compiler – it compiles the code above regardless that it can’t ever work. It will always throw this error: “BaseClass object does not have method ‘childOperation'”.
I have one more extreme example for you:
Object o = new Dialog(); TextIo textIo = o;
You see that we work here with completely unrelated types, but the code is still compilable and runs. The compiler shows the crazy truth – you have a variable of TextIo type containing a Dialog.
Don’t believe that an assignment is correct just because the compiler allows it – check types by yourself. In AX 2012, you have is and as operators for this purpose, therefore a better implementation could look like this:
if (base is ChildClass) { child = base; }
Add a pinch of CIL…
There is one more point to make. If you compile X++ code to CIL, it will run in a different runtime environment (CLR) which is stricter than AX. In our sample code, we saw that AX accepts the assignment on line two and throw an error when calling an non-existing method on line 3:
BaseClass base = new BaseClass(); ChildClass child = base; child.childOperation();
But if you compile this to CIL and run it in CLR, the assignment itself will fail, because the types are not compatible. You’ll get this error: System.InvalidCastException: Unable to cast object of type ‘Dynamics.Ax.Application.BaseClass’ to type ‘Dynamics.Ax.Application.ChildClass’.
It’s not merely about when you get an error – the following code actually works in X++ but it fails in CIL:
BaseClass base = new BaseClass(); ChildClass child = base; child.name();
The type of child variable is ChildClass and the compiler allows you to call method of ChildClass. In X++, the assignment (child = base) succeeds and child actually contains an instance of a different type (BaseClass in this case). AX still tries to call the method and if the actual object has a method of the same signature, it gets called. In our example, child.name() returns “Base” – it works, but the behavior is wrong.
Although the code runs in AX, the assignment is still invalid from CLR perspective and the execution of CIL will fail.
Conclusion
Fortunately, all these things shouldn’t cause you too many troubles if you write your code reasonably. But you may sometimes have incorrect assumptions about the type returned by a method and you get an unexpected type assigned to your variable. Because it doesn’t get detected at this point, both you and the compiler keep the false assumption and you later don’t have a clue why the runtime complains about a missing method. I’ve seen several people struggling with this problem when working with .NET Interop from X++. And the difference between AX runtime and CLR adds some additional complexity.
Hi Martin, tnx for this article. It clearly points to danger parts of X++ compiler. br Beat
Hi Martin,
Thanks for posting this article. I am facing the same issue in SysDataImport class dataImport method where DictTable (Parent class) object is assining to SysDictTable (Child Class). This code working fine in AX but gives me an error while the class is runing in BatchJob.
Error : “Unable to cast object of type ‘Microsoft.Dynamics.Ax.Xpp.DictTable’ to type ‘Dynamics.Ax.Application.SysDictTable’.”
Is there any workaround for down casting ?
Regards,
Janak
You can use the “as” operator ( dataImport() as SysDictClass ).
Hi Martin,
I have tried the as operator as per your recommendation. My code is as below. But still getting same error.
SysDictTable tmpDictTable;
DictTable dictTable;
tmpDictTable = this.setupTableImport(tableId);
dictTable = tmpDictTable as SysDictTable ;
dictTable variable getting null
can you help me in this?
Regards,
Janak
You use the “as” operator on the wrong assignment. You have to do it where it fails, i.e. the line above. You used in on an assignment that always succeeds anyway.
By the way, a discussion forum is better suited for such a discussion. I’m present in community.dynamics.com and dynamicsuser.net.