Generics in .NET Interop for NAV 2013 – 5/17, Navigate Into Success

image.NET Framework is full of programming conceptual gems, that are now at the fingertips of us poor C/AL folks. One of those is generics. However, the C/AL support for generics at the first glance seems rather limited, and the help file says that you can’t specify data types, and that all generics will be instantiated with System.Object as their type.

However, with Microsoft Dynamics NAV 2013, there is a very simple way which allows you to use generics with other data types, as well. So, if .NET Framework Interoperability interests you a slightest bit, here’s a solution.

The example below will be for the System.Collections.Generic.Dictionary<,>, and I will show how to use instances of the Dictionary<,> object with any desired data type, without having to pull in any external assemblies.

The Solution

Before I show the example, I’ll first explain the solution.

Declaratively, you can’t declare a generic variable, and specify the type. C/SIDE is just not (yet) that flexible. But that doesn’t matter, because .NET Framework includes a nice feature which allows you to create instances of any type on the fly: Reflection.

By using reflection, you can create an instance of a generic type, and specify which type(s) it should use, all with very little coding. In the examples that follow, I’ll create an instance of Dictionary<string,int>.

The whole process in C# would look, more or less, like this:


Dictionary<string, int> dict =

    Activator.CreateInstance(

        typeof(Dictionary<,>).MakeGenericType(

            new Type[]

                {

                    typeof (string), 

                    typeof (int)

                })) as Dictionary<string, int>;
 
Okay, it’s kind of hax0rish, because all is inline, so if you prefer it step by step, here it goes:

// Step 1

Type[] types =

    new Type[]

        {

            typeof (string),

            typeof (int)

        };

 

// Step 2

Type dictionaryType = typeof (Dictionary<,>).MakeGenericType(types);

 

// Step 3

Dictionary<string, int> dict = 

    Activator.CreateInstance(dictionaryType) as

        Dictionary<string, int>;
 

Step 1 creates an instance of a 2-element array of Type. This is needed for creating a generic type using reflection.

Step 2 uses reflection to create a generic type of specified types. The types are specified in the array we created in the step 1.

Step 3 uses reflection to create an instance of the type created in step 2.

Now that we’ve seen it in C#, let’s map the same to C/AL.

 

1. Declaration

Let’s get it straight, you can’t declare a DotNet variable of a generic type, and specify the actual type (or types) it generalizes. The C/SIDE simply doesn’t allow that. But, don’t worry. Go ahead, and declare the following variables:

Name Subtype
Dict System.Collections.Generic.Dictionary`2.’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Type System.Type.’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Activator System.Activator.’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Arr System.Array.’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
String System.String.’mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Int  

Of course, all of the above are DotNet, except for the last one, which is Integer.

 

2. Creating an instance of an array

To declare an instance of an array of type Type, we can call the CreateInstance method of the Array class, by providing the type of System.Type. Then we populate the array with the types of System.String, and System.Int32:


Arr := Arr.CreateInstance(GETDOTNETTYPE(Type),2);

Arr.SetValue(GETDOTNETTYPE(String),0);

Arr.SetValue(GETDOTNETTYPE(Int),1);

This isn’t nearly as elegant as in C#, but accomplishes the goal quite as nicely. Take a note of the GETDOTNETTYPE function – a new gem in C/AL, the equivalent of the typeof keyword in C#.

 

3. Creating the dictionary type

To create the type for the Dictionary<string,int> that we need, we have two steps. First is to get the type of Dictionary, and the second is to use that type to reflect out the actual Dictionary type that we need, based on types specified in the array:


Type := GETDOTNETTYPE(Dict);

Type := Type.MakeGenericType(Arr);

Again, it can’t be a single line in C/AL, because the syntax of C/AL does not treat the result of the GETDOTNETTYPE as an object.

 

4. Creating an actual instance

Finally, we create an actual instance of the Dictionary<string,int> object, exactly as we would in C# (except we do it in C/AL):

Dict := Activator.CreateInstance(Type);

There. And now we are ready to use it.

 

5. Testing if it really is what we need

Testing generics is easy – if you pass on to it the arguments of invalid type, they’d complain loudly. So, let’s try to pass some valid ones, and an invalid one:


Dict.Add('first',1);

Dict.Add('second',2);

Dict.Add('third','three');

Here, at the 3rd line, it fails with the following message, exactly as expected: This message is for C/AL programmers: A call to System.Collections.Generic.Dictionary`2[System.String,System.Int32].Add failed with this message: The type of one or more arguments does not match the method’s parameter type.

So, obviously, we have an actual instance of the Dictionary<string,int> which receives exactly those elements, and behaves exactly as we would expect from a true .NET Framework generic class: if we try to pass a value of incorrect type, it’s not going to be happy.

 

6. And now for something completely different

If you know anything about generics, at this moment you should be puzzled, as I was when I first tried this out. All of the C/AL voodoo above is the equivalent of this code in C#:


Dictionary<object, object> dict =

    Activator.CreateInstance(

        typeof (Dictionary<,>).MakeGenericType(

            new Type[]

                {

                    typeof (string),

                    typeof (int)

                })) as Dictionary<object, object>;

No need to try running it, it fails. Actually, it doesn’t fail, it returns null. The problem is, being strongly typed, the .NET runtime can’t cast Dictionary<string,int> as Dictionary<object,object>, and the declared type of Dictionary<object,object> can’t hold a value of Dictionary<string,int>. It’s apples and oranges—even though both are instances of the same generic type, it’s not the same actual type, and they are not typecast compatible.

When you declare a DotNet of a generic type, in this case the Dictionary type, it’s declaratively Dictionary<object,object>, so how in the earth did we manage to stuff Dictionary<string,int> into it?

As a matter of fact, we never did that. I can’t say for sure, but I’ll give an educated guess here. DotNet is never actually the exact type we declare, but a wrapper class around just about anything. I assume it actually wraps around System.Object, and then uses reflection to access the members of the actual object it wraps.

But in the end, why do we care? It does what we need it to do.


Read this post at its original location at http://navigateintosuccess.com/blog/generics-in-net-interop-for-nav-2013, or visit the original blog at http://NavigateIntoSuccess.com. )..

Continue reading on Source Blog