Indexed properties are commonly used in C# because they allow a lot of syntactical flexibility, and make the code more readable, and easier to follow. Indexed properties are very similar to C/AL array indexing, except for two important differences:
- In C/AL, indexer is always 1-based. In C#, indexers are 0-based.
- In C/AL, indexer is always an integer. In C#, indexers can be any type.
These two examples show these differences:
In my last post, I have used an indexed property to enable C/AL to set any property value on the inner control of a Control Add-in for the RoleTailored client. As I said, in C#, indexed properties are natural, and they enable C# access such as this:
As a reminder, this is how implemented this indexed property:
(In this post I will simply continue building on top of the code I wrote in the last post, so you may want to check it out: https://vjeko.com/blog/trick-instantiating-any-control-in-a-control-add-in)
Yes, I know – you may ask if this will work in C/AL. And the question is in place, really. We have seen that C/AL is a bit stubborn when it comes to mapping handy and simple C# syntax constructs to it. So, do indexed properties work in C/AL? They do, and they don’t. Let’s compile the C# code and see what the Symbol Menu shows:
This “Item” is a funny guy. You never declared anything like this in C#, and now it is there. Also, this is how the Symbol Menu shows its syntax:
You may think this is the Development Environment’s work, but it is not. Once you declare an indexed property, the .NET Framework (in fact, the C# compiler) add a built-in member, named Item, to support access to the indexer for those languages that don’t know how to work with indexers. C/AL simply makes use of this member.
So, to access the indexed property in C/AL, you can do this:
As any other thing in C#, indexed properties can be overloaded. This means that different methods using the same name can exist in the same class, as long as their signatures are different. So, C# allows this:
Normally, C/AL understands overloaded members, and according to C/AL documentation, C/AL should understand overloaded indexers as well. But, does it? Let’s compile that C# piece of code and take a look at the Symbol Menu. On my machine (typical programmer’s excuse, eh ) I see this:
Obviously, C/AL does not understand overloaded indexers. This is a flaw in C/AL implementation (no plausible reason for this not to work), and documentation (it says it works, and it doesn’t). If you compare the C# and the Symbol Menu, obviously, the C/AL has given advantage to the property with indexer of type int. Why not to the one with the type string? Is there anything special to int? No, there isn’t. It’s all about the order. So if you want your string indexer to show in C/AL instead, just do this in C#:
And the Symbol Menu will happily oblige:
So, unfortunately, and at least until this bug gets fixed (I hope it’s not only the documentation that gets fixed), you can only use one indexed property per class, and that would always be the last property listed in C# code.
I’ll now take the int-based indexed property, and focus on the name under which the property is shown in the Symbol Menu. It’s “Item”. Why is it “Item” and does it have to be “Item”? Can it be anything else?
By default, C# compiler turns every indexed property into a named member. Unless you instruct it otherwise, this named member is named “Item”. This makes it possible for those languages that don’t know how to use indexers to still access those indexed property, while allowing C# to use it in a syntactically preferred way (when you compile a piece of C# code that uses the indexed property, it uses the named member itself).
C/AL is one of those languages not preferring the indexer syntax, and it makes use of the Item property. However, this Item property may seem counterintuitive and may make code less readable or easy to follow. If you take a look at this, it shows exactly why it is not the best possible name:
We use this code to set a property, and here we refer to it as “Item”. Wouldn’t this make much more sense:
Yes, it is possible. To instruct the C# compiler to rename the indexer, you simply need to decorate the property with the IndexerName attribute from the System.Runtime.CompilerServices namespace. Just like this:
This readily propagates to C/AL, and the Symbol Menu is more than happy to show this:
Aha! – you may think at this point. Can’t we just do this:
Well, it would be really, really cool if we could, but it turns out – we can’t. The C# compiler is pretty strict about this, and it says: “Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type”.
You may think this is just an arbitrary error by C# compiler, and there must be a workaround, but it’s not, and there isn’t. The reason why this does not work is that the IndexerName attribute is inaccessible to reflection, and the only way for C# compiler to be able to translate indexer syntax into actual member access would be guessing. And C# never guesses.
Here endeth the lesson.