Introduction

The first impression of most developers I’ve encountered when they experienced indexers is, they felt like it was an array. It is because instances of a class or struct which uses indexers are behaving like an array. However; don’t be confused -- indexers aren’t arrays, but they appear to be. Let us try to answer, what is an indexer? In the next section.

What is an Indexer?

Indexers are like properties which accept arguments and indexer object can be indexed in the same way an array does. In addition, because it is like properties it can have access modifiers such as public, private, protected or internal. Then last but not least, we can’t ignore the getters and setters of course. The purpose of the indexer is basically to simply the syntax (syntactic sugar).

The difference between Properties and Indexers

Properties Indexers
An instance or static member Instance member only
Accessed via a name Accessed via an index
Getters has no parameters Getters has the same formal parameter list as the indexer
Setters uses implicit value parameter. Setters of an index have the same formal parameter list of the indexer and to the value parameter.
Supports auto-implemented properties Doesn’t support auto implemented properties.

When to use Indexer?

  • If you need a collection of its instances.
  • If you need a collection of values directly related to your instance.
Examples

Framework Common Library Which Uses Indexers

In this section, we will see the common libraries that are using the indexer feature.
Just a note. I tried to explore this so at least we are aware which libraries of the .NET Framework are using the indexer feature.
Please, see the code below, so we could extract a list of libraries that use this feature.
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using Microsoft.VisualStudio.TestTools.UnitTesting;
  5. namespace UnitTestProject1 {
  6. [TestClass]
  7. public class LIbraryUsesIndexers {
  8. private const string FOLDER_NAME = "Microsoft.NET\\assembly\\GAC_64\\mscorlib\\v4.0_4.0.0.0__b77a5c561934e089\\mscorlib.dll";
  9. [TestMethod]
  10. public void Test_MSCORLIB_Get_Libraries_Uses_Indexer_Feature () {
  11. string initPath = System.Environment.GetFolderPath (Environment.SpecialFolder.Windows);
  12. string fullPath = Path.Combine (initPath, FOLDER_NAME);
  13. var mscorLib = System.Reflection.Assembly.LoadFile (fullPath);
  14. foreach (var itemType in mscorLib.DefinedTypes) {
  15. var type = itemType.GetProperties ().Where (x => x.GetIndexParameters ().Length != 0).FirstOrDefault ();
  16. if (type != null) {
  17. Console.WriteLine ($" Type: {itemType.FullName}, Property: {type}");
  18. }
  19. }
  20. }
  21. }
  22. }
Please see the table for the list of libraries as the result of the above code sample.
Type Property
System.String Char Chars [Int32]
System.ParamsArray System.Object Item [Int32]
System.Security.AccessControl.GenericAcl System.Security.AccessControl.GenericAce Item [Int32]
System.Security.AccessControl.RawAcl System.Security.AccessControl.GenericAce Item [Int32]
System.Security.AccessControl.CommonAcl System.Security.AccessControl.GenericAce Item [Int32]
System.Security.AccessControl.SystemAcl System.Security.AccessControl.GenericAce Item [Int32]
System.Security.AccessControl.DiscretionaryAcl System.Security.AccessControl.GenericAce Item [Int32]
System.Security.AccessControl.AuthorizationRuleCollection System.Security.AccessControl.AuthorizationRule Item [Int32]
System.Security.Principal.IdentityReferenceCollection System.Security.Principal.IdentityReference Item [Int32]
System.Security.Policy.ApplicationTrustCollection System.Security.Policy.ApplicationTrust Item [Int32]
System.Diagnostics.Tracing.SessionMask Boolean Item [Int32]
System.Diagnostics.Tracing.EventPayload System.Object Item [System.String]
System.Collections.ArrayList System.Object Item [Int32]
System.Collections.BitArray Boolean Item [Int32]
System.Collections.ListDictionaryInternal System.Object Item [System.Object]
System.Collections.EmptyReadOnlyDictionaryInternal System.Object Item [System.Object]
System.Collections.Hashtable System.Object Item [System.Object]
System.Collections.IDictionary System.Object Item [System.Object]
System.Collections.IList System.Object Item [Int32]
System.Collections.SortedList System.Object Item [System.Object]
System.Collections.Concurrent.ConcurrentDictionary TValue Item [TKey]
System.Collections.ObjectModel.Collection T Item [Int32]
System.Collections.ObjectModel.ReadOnlyCollection T Item [Int32]
System.Collections.ObjectModel.ReadOnlyDictionary TValue Item [TKey]
System.Collections.ObjectModel.KeyedCollection TItem Item [TKey]
System.Collections.Generic.Dictionary TValue Item [TKey]
System.Collections.Generic.IDictionary TValue Item [TKey]
System.Collections.Generic.IList T Item [Int32]
System.Collections.Generic.IReadOnlyList T Item [Int32]
System.Collections.Generic.IReadOnlyDictionary TValue Item [TKey]
System.Collections.Generic.List T Item [Int32]
System.Reflection.ConstArray Byte Item [Int32]
System.Reflection.MetadataEnumResult Int32 Item [Int32]
Just a note about list of libraries. I did remove the backticks and the succeeding number to show clarity.
For example System.Collections.Generic.List`1 was converted into System.Collection.Generic.List.
Also, just a note, you might be wondering “why does it have a backtick and followed by a number?” To answer that.
  • Names of generic types ends with a backtick (`).
  • Then followed by digits. These digits are representing the number of generic type arguments.
  • The main purpose of this naming is to allow compilers to support generic types with the same name but with different numbers of type parameters.
Remarks
We can say that C# indexer is just syntactic sugar to help developers to encapsulate internal collections or arrays.
Hopefully, you have enjoyed this article.
Lastly, you can download the sample code here: https://github.com/jindeveloper/Csharp-Indexer-Feature.
Happy programming, until next time.