Size matters

X++ normally doesn’t distinguish letter case – you can call warning(“x”) or WaRnInG(“x”) and both will do the same (although your colleagues may not be as forgiving as the X++ compiler). Nevertheless the same doesn’t apply to .NET Interop from X++ – although you can use System.Math::Cos(0), for example, you’ll get a compilation error if you try to call System.Math::cos(0).

There are even situations when a change in casing results in a call of a different method. Look at the following piece of code – it causes a .NET exception, catches it, converts it to a string and sends it to infolog:

try
{
    System.IO.File::Open('no such file', System.IO.FileMode::Open);
}
catch (Exception::CLRError)
{
    error(CLRInterop::getLastException().ToString());
}

The output should be something like: “System.Reflection.TargetInvocationException: Exception has been thrown by the target of invocation. —> System.IO.FileNotFoundException: Could not find file ‘C:\Windows\system32\no such file.’ (…)”

Now change ToString() to tostring(). The output will be “Class CLRObject”.

What happened? What we get from CLRInterop::getLastException() is an instance of CLRObject (X++) class, which represents a .NET object (FileNotFoundException in this case). When we call ToString() method, AX is smart enough to recognize that FileNotFoundException contains such a method and calls it. But if we call tostring(), AX can’t use the method on FileNotFoundException due to case sensitivity. But there is toString() method on the CLRObject class itself (inherited from Object) that can be called without issues. Therefore we’re not dealing with a single method returning different results, what’s called are methods on completely different objects.

It may be a bit confusing but it makes sense.