“Casting” .NET Objects in Dynamics NAV

Let me start this off with saying this is not a post about how-to cast .NET objects from one type to another in C/AL code. I don’t know how to do it yet, or if it is even possible, I’m still playing around with some .NET libraries… (maybe next blog post… 😉 ). This is more a post about what was actually happening when it looked like NAV was successfully casting (hence the “”) but it wasn’t really.

In my last post I was trying to get at the properties of a Collection that inherited IEnumerable, therefore I assigned it to an IEnumerable type and trusted in the tenets of Polymorphism to give me the correct underlying property. In .NET this is an example of up-casting – which I try not to think of as casting as it sort-of doesn’t change the underlying type.

Along the way I made a mistake in my C/AL code and accidentally assigned the Collection directly to an Array. I was slightly surprised when NAV happily compiled this, and then I was even more surprised when it didn’t complain about the assignment at runtime – it only complained when I tried to use the Array as an Array.

To see what is going on I created a basic test. NAV happily compiles and runs this code.

C/AL
DotNetArray := DotNetArray.CreateInstance(GETDOTNETTYPE(DotNetString),2);
DotNetArray.SetValue('Hello World',0);

DotNetString := DotNetArray;

This is not something that would compile in C#. You cannot implicitly or explicitly cast Array to String.

DotNetString = DotNetArray;

Next we try to get at the properties of the Array

C/AL
MESSAGE('IsReadOnly = %1',DotNetString.IsReadOnly);

This predictably does not compile in NAV. While the Array has a property called IsReadOnly, the String class does not.

C/AL
MESSAGE('Chars = %1',DotNetString.Chars);

This does compile (String has a property called Chars), but at runtime you will get an error:

A call to System.String[].get_Chars failed with this message: The type of one or more arguments does not match the method’s parameter type.

C/AL
MESSAGE('Length = %1',DotNetString.Length);

This one is interesting – both String and Array both have a property called Length. So it both compiles and runs. And the result shows it is calling the function on the Array.

Length = 2

So what it is doing. Well, once again, Vjeko has beat me to the punch (will all my blog posts reference Vjeko? probably…):

A variable of type DotNet is translated into C# not as the declared type, but as the NavDotNet type. It’s a wrapper type that handles various things around DotNet interoperability, and your DotNet variable actually lives as an instantiated instance of the declared type… My educated guess here is that NAV is using reflection to instantiate your actual variable

And looking at the generate C# from my code above (with a lot cut out to simplify) it becomes more clear.

C#
private NavDotNet dotNetArray;
private NavDotNet dotNetString;

private void InitializeComponent()
{
dotNetArray = new NavDotNet(this, @"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", @"System.Array", false, false);
dotNetString = new NavDotNet(this, @"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", @"System.String", false, false);
}

protected override void OnRun()
{
dotNetArray.ALAssign(dotNetArray.InvokeStaticMethod<NavDotNet>(@"CreateInstance", 30U,
NavDotNet.GetDotNetType(this, dotNetString), 2));
dotNetArray.InvokeMethod<Object>(@"SetValue", 78U, @"Hello World", 0);
dotNetString.ALAssign(dotNetArray);

NavDialog.ALMessage(new Guid(...), @"Length = %1", ALCompiler.ToNavValue(dotNetString.InvokePropertyGet<Int32>(@"Length", 1U)));
NavDialog.ALMessage(new Guid(...), @"Chars = %1", ALCompiler.ToNavValue(dotNetString.InvokePropertyGet<Char>(@"Chars", 0U)));
}

NAV’s C# is using InvokeMethod and InvokePropertyGet which are Reflection methods, and the ALAssign function doesn’t seem to have any problem replacing one type with another.

So, what does this all mean? Well, it means we are not really casting from one type to another.

Are there any benefits? My last blog post showed that there are i.e. if you cannot access a .NET object for some reason in NAV, if you can find any other one with a property or function with the exact same method signature then you can just assign it in NAV (to make the compiler happy) and then call the property. It uses Reflection anyway so you could manually use the Reflection methods but this is easier.

The main benefit is really that it makes it look like Polymorphism works. Which, putting on my coding-standards hat, is probably the only way you should use this, just in case Microsoft decide to tidy this workaround up and make it all a bit more strongly typed 😀

Nikolai

Advertisements

One thought on ““Casting” .NET Objects in Dynamics NAV

  1. If this wasn’t flawed (in my opinion), you could say that this is polymorphism on steroids 🙂 Or you could call it C#Script – just like in JavaScript, your variables change the type as needed 🙂

    Of course, I am only kidding. It would really be great if Microsoft “fixed” all those issues, but I don’t really expect them to. They wanted to enable basic access (which they did) and allow us to write wrappers for anything more complex than that (which we can). So, as far as you ask them – mission accomplished.

    Thanks for mentioning me, by the way 🙂

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s