If you ask me what the top addition to the NAV technology stack over the past few years is – it’s .NET interoperability. A lot of folks, maybe you as well, would disagree, and say it’s Web services. They are important. But if you are a NAV developer, Web services don’t make your life any easier. You are programming for Web services when your requirements tell you so, but that’s it. You don’t experience those moments of truth, when it dawns on you, when you go eureka, slap your forehead and say: now this is something I solve with Web services! Not quite.
But with .NET interoperability, it’s a different story. If you know how to harness its power, there is no single project you’ll ever want to go without using .NET. It opens the door to the most powerful development framework for Windows, and it makes many impossible things possible, in pure C/AL.
There are two kinds of things in this world. Those that .NET Interoperability can do, and those it can’t. Microsoft has been steadily improving it since the initial release in 2009 R2. However, there is still much to be desired. Those small things that you cut in C# in seconds, and twist your brain inside out for hours before you realize you can’t do it in C/AL. Some of them may be in a backlog somewhere in Vedbæk, but I don’t know that, so I decided to compile a list of top 10 things I believe C/SIDE should support, and it doesn’t.
Knowing what I think I know of C/AL, C/SIDE, C#, and .NET, I’ll not only say what I miss, but I’ll give my angle at how difficult or feasible (if at all) I believe it would be for the NAV team to actually implement each feature.
I’ll start from #10, just to add a bit of suspense
#10 Static Variables
You can create variables of type DotNet, but all of them are instance (non-static, or dynamic). The only way to have a static variable is to create your own assembly, declare a class as static, or declare static fields or properties on an instance class, but it’s a lot of work and is not flexible. .NET itself is flexible enough that it allows you to declare any private or public member, including properties and fields, as static.
Now, maybe it’s an intentional decision by NAV team not to allow this, and I can see two reasons for this. First, static classes are effectively singleton per application domain, which means they stay in memory for the duration of the process that hosts them. This could introduce many risks, but I don’t think that this is relevant. Preventing users from having static variables of an arbitrary type does not achieve anything, because they still can go and create a static class of their own, pack it into an assembly, and voilà!
I suspect it’s the second reason. 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. But the key here is instantiated. My educated guess here is that NAV is using reflection to instantiate your actual variable, and if it does, then there’s the core of the problem. You cannot instantiate a static instance. Now this sentence is so oxymoronic that it should be hanged, drawn, and quartered, not necessarily in that order, but it does illustrate vividly why something like this is impossible.
Something else might be possible, though. Since every DotNet variable is defined as a member field of its parent object or scope class, maybe the whole NavDotNet field could be declared as static? It may or may not be a simple change for the NAV team, because every DotNet variable lives as a field of a disposable class (through IDisposable interface). I don’t have a clue whatever happens in the Dispose method in there, but my hunch tells me that it manually disposes of your DotNet variable, just in case.
So, it certainly wouldn’t just be as easy as adding a new variable property for DotNet variables, and that’s why I am not giving it higher priority in my list either. I can live without it, but life would be simpler if it were there.
Feasibility? Difficult, if at all possible.
#9 Member of member access for directly mapped types
This is an extremely annoying non-feature. In .NET, you can access member of a member of a member, of any class, for as long as there are members of members. Just like this:
However, C/AL doesn’t handle this well. This:
results in this:
No good. Similar with the DateTime. C# handles this well:
And NAV falls apart at this:
Again, it’s the same error.
For most types you can safely call members of members, however when you get at System.String or System.DateTime, System.Guid (and possibly some more types but I just couldn’t bother to test them all), it stops there, and doesn’t process any further members, because in C/AL, they are simple data types.
The reason why this happens is because the C/AL compiler automatically treats System.String, System.DateTime, System.Guid, and possible some more .NET types which directly map to native simple C/AL types, as native simple C/AL types.
It shouldn’t be difficult to fix this thing, as this is more of a bug, than a feature.
And while fixing that, why not enable something like this, as well:
This one is funny. We have ISNULL function to determine if something is null. But, we don’t have a constant or anything to actually represent null. Yes, there is a workaround for this: declare a DotNet variable of subtype System.Object, name it null, and never assign any value to it—it even looks like null in the code. But let’s get serious here – it’s a dirty trick. And depending on whether it’s a global or a local variable, somebody may just accidentally assign a value to it. Or intentionally. And you don’t have null anymore.
Why is this important? For all those situations when you need to pass null into a method as a parameter value.
About feasibility – this is a morning exercise for an Associate Software Engineer, while chatting on Facebook and eating a sandwich.
#7 foreach enumeration
Enumerations are very common in .NET, as they enable a very nice C# language concept around them: the foreach loop. C/AL does not know of this type of loop.
C# is very elegant about it:
(it’s the foreach loop that I am referring to, not the initializer block, which is also quite elegant).
NAV is a bit less elegant here:
The foreach loop is not a .NET feature, but a C# compiler feature which allows any class that implements the IEnumerable interface to be, well, enumerated. The compiler translates the foreach loop into the pattern I showed above in C/AL: it first gets the enumerator, and then calls the MoveNext and Current members of the IEnumerator interface to navigate the collection.
Now I’ll admit that this really is whining from me, asking for a feature such as this, because we have a working workaround, whose only downside is that it requires us to declare an extra variable to iterate through the collection.
But still, from feasibility perspective, it’s a Friday afternoon exercise. And wouldn’t this be cool?
#6 DotNet as return type
Functions can only have simple data types as return parameters, and it has been like that since forever. I have never fully understood why. I could bet that Thomas and Freddy occasionally, over a beer, bring this back to the table, and then dismiss it for some reason known only to them.
Well, I can settle for Record and Page and Codeunit and a score of other types that do not make all that much sense as return types. But DotNet? Hey!
When it comes to feasibility, I am not quite sure how difficult this would be to implement. My educated guess is that it should be fairly simple. A lot of legwork, but simple.
If you know what generics are, chances are that you are asking me now – why only #5, why not #3, #2, or even #1. It’s simple – because at least there is a workaround. However, the workaround is very verbose and requires you to pull some funky tricks from under your .NET belt.
Unlike static variables, I believe generics would be a piece of cake for the NAV team. The only thing they need to do is to add an extra property for any DotNet variable over a generic type. I’d call this property Types, and it’d have an AssistEdit button that takes you to a list of up to n .NET type references, where n is equal to the number of types the generic type uses. It would be simple, easy, straightforward, and so unbelievably powerful that I can’t stop thinking of all the crazy—in a very positive sense—things we could all suddenly do with .NET Interoperability.
This is low-hanging fruit, Mr. Microsoft! Think of how easy this is to implement!
#4 Conversions and typecasting
C# is an example of a supercalifragilisticexpialidociously wonderfully fantastically ingeniously versatile programming language. It allows types to be directly converted to other types, explicitly or implicitly. You can have your Student class which allows implicit conversions of string to it, which results in amazing coding elegance. Like this:
When you do something like this in NAV, you get this:
C# allows three types of conversion. Implicit, explicit (casting) and reference-type explicit casting (the as keyword). C/AL supports none of these, except for casting, up, and down the inheritance tree. Up-casting is only a natural effect of polymorphism, but it’s the explicit casting down the inheritance tree that makes me go bonkers. Because C/AL happily supports it, and there is no reason why it should. This, interestingly, compiles:
And it fails in C# (as it by all means should):
This can be an inadvertent bug, due to the implementation of the wrapping NavDotNet class in NAV, but it also can be an intentional feature. If it’s the latter, and if it obviously is there, why not allow conversions and typecasting in general?
I am certainly not whining when I say implicit (and explicit, as well) conversions should be allowed in C/AL and there is a strong reason why this is so high on my top list of missing features. There are .NET classes that fully depend on implicit conversions. If you cannot implicitly convert to them, then you cannot even use them, unless you write wrapper classes.
One notable example is the XName class from the System.Xml.Linq namespace, which directly depends on implicit conversion from string. If you can’t convert, you can’t use it. Which means you can’t use the XDocument class either, and that’s a pity.
And there must be thousands of more examples.
For this feature, it could get a bit tricky around implicit conversions, as it would probably have to involve some funky reflection, but possible? Absolutely.
#3 Extension methods
Extension methods allow you to add functionality to existing classes. If you know extension codeunits in Web services, well, this is not quite the same thing, but is analogous. Here’s an example of an extension method:
Okay, it’s not particularly powerful at that, but it’s just an example. And no, something like this is not possible in C/AL.
Whether or not C/AL should support extension methods is arguable. Extension methods are not a facility of .NET, but another feature of Visual Studio. However, there are so many extension classes—better yet, whole libraries—that do nothing but deploy hundreds of extension methods. Take LINQ for example. No, you cannot use LINQ in C/AL. Technically, you could use the static classes directly, but it would just be crazy.
Related to how difficult it would be for the NAV team to implement that, it’s probably a different story. As I said, extension methods are not native to .NET, and C# and Visual Basic compilers must do some voodoo to make this possible. From my educated guess that most of DotNet stuff is actually reflected, it would mean that implementation of extension methods would be beset by obstacles. It would probably make the compiler considerably slower, because it would have to do many extra checks. Or maybe not. Who knows, or dares to dream…
Regarding how to reference extension classes, this part could be simple. In the object properties, simply add two properties: AssemblyReferences, and Using. And then apply available extension methods to any classes that want to use them.
#2 Boxing, unboxing and casting to System.Object
Boxing is an important concept in C# and Visual Basic, but it’s yet another C# and Visual Basic compiler feature, not a .NET feature. It’s a fairly useful feature in C#, and I have missed it many times already. Funny thing is – it already works, only partially.
Look at these examples:
Why the first group works, and second fails, beats me. In the first one I have successfully boxed 6 values, though indirectly (by passing a value to a .NET method which expects an object), and in the second example I was unable pass a value to C/AL function that does the same. Also, I was unable to directly box into a System.Object variable. I won’t provide any code example, but take my word for this: you also cannot directly unbox from an object variable into a C/AL type.
Is this a bug, or a feature? Probably something in between.
But there certainly are bugs when it comes to boxing. Take a look at this:
If you run it, you’ll see that after you unbox the date formula from the dictionary, it does not contain the same value. No error, just not the right value. I took date formula because it’s one of those types that are neither simple, nor complex, and I suspected boxing may not be able to digest it properly.
But now we get to the real deal. Casting NAV complex types to System.Object. It should really be no rocket science, because System.Object is the mother of all classes, quite literally. So why is this not possible?
The compiler doesn’t mind, but the runtime gets a bit grumpy:
Now this could easily be intentional. Maybe Microsoft doesn’t want us to cast a Record to System.Object and then reflect the crap out of it. I could understand that. It’s wrong, and kind of cheating, but I could still understand.
Nevertheless, everything about #2 could, and should be fixed. Making this work, oh my. I am drooling all over my laptop already. Things like this could make C/AL so incredibly powerful. Yes, I know, there is a downside—we could easily drain the server out of memory. But, Mr. Microsoft, can it be us, please, to worry about our resources? (If that’s the reason at all why this specific thing is not allowed).
About feasibility? I don’t honestly believe it could be all that difficult now, would it?
And finally, the missing feature that beats them all, that overshadows everything else on this list so powerfully that if Microsoft provides no other improvement to .NET Interoperability ever again, but support for delegates alone, I would die a happy man. Okay, not really, because if I died immediately thereafter, I wouldn’t get much chance to enjoy all the amazing benefits C/AL delegates would bring, but it would still be a big, big, BIG improvement.
MSDN provides a simple definition of delegates. A delegate is a type that references a method. So, what’s a big deal?
In the example above I have a class which sorts an object array using bubble sort. Since objects can be anything, the Sort method does not want to attempt to sort itself, so it delegates the work to another method. You pass that method as a parameter to the Sort method. Then, bubble sort simply invokes this parameter as a method, because, in fact, it is a method reference. The consumer class has a method which has exactly the same signature as the delegate type, and it passes the reference to that method as a parameter to the Sort function. That’s delegates.
Now, wouldn’t this be beautiful:
Delegates are extensively used all over .NET, there are myriads of methods which take delegates as parameters and all those methods are out of reach of C/AL. Most of LINQ, for starters.
And no, I am not whining about lambdas, we don’t need them. Just the delegates.
Now, delegates look pretty tricky, don’t they? You’d probably think it would take some black magic before delegates could be made possible, but it’s nothing like that. The Compare function from the example above (the fictitious NAV example) actually looks like this in the translated C# class file:
Pretty much delegable if you ask me. Making delegates a reality in C/AL, in all likelihood, would be as easy as a pie.
I hope you enjoyed this list – and please, share your thought. What’s your top missing feature?