DEV Community

DotNet Full Stack Dev
DotNet Full Stack Dev

Posted on

Indexers in C# โ€” Making Your Classes Feel Like Arrays

Hello, .NET developers! ๐Ÿ‘‹

Have you ever accessed array elements using square brackets โ€” like arr[0] โ€” and wished you could do the same for your own classes? In C#, you actually can โ€” using indexers. They allow you to make objects of your class act like arrays, providing a natural and readable way to access internal data.

Letโ€™s dive deep and see how indexers work, why they exist, and how they can simplify your real-world code.


DotNet Full Stack Dev - YouTube

Our Goal : We aim to deliver clear and simple videos that anyone can easily understand. Our Philosophy : We embrace simplicity. Our content is designed to be accessible to all, cutting through complexity to present information in a direct, straightforward manner. Our Content : Browse our channel for videos focused on clarity and practicality. We simplify complex topics into bite-sized, digestible pieces to ensure our content is both valuable and easy to follow. Our Commitment : Simplicity drives our commitment. We provide information that is not only insightful but also immediately applicable, ensuring viewers can extract practical takeaways from every video.

favicon youtube.com

What Is an Indexer?

An indexer allows an object to be indexed like an array. It defines how your class responds when someone uses the [] operator on its instances.

Think of it as giving your class a way to โ€œbehave like a collection,โ€ without actually inheriting from one. You can use integers, strings, or even custom objects as the index keys.

Simple Example

public class StudentCollection
{
    private string[] _students = new string[3];

    public string this[int index]
    {
        get { return _students[index]; }
        set { _students[index] = value; }
    }
}

class Program
{
    static void Main()
    {
        var students = new StudentCollection();
        students[0] = "Bhargavi";
        students[1] = "Anita";
        students[2] = "Rohan";

        Console.WriteLine(students[0]); // Output: Bhargavi
    }
}

This looks and feels exactly like an array, but itโ€™s actually a custom class. The this keyword, followed by an index parameter, defines the indexer. You can get or set values just like arrays โ€” thatโ€™s the power of syntactic sugar in C#.


How It Works Behind the Scenes

When you write students[0], the compiler silently converts it into a method call like:

students.get_Item(0); // Getter
students.set_Item(0, "Bhargavi"); // Setter

This means indexers are essentially a special kind of property โ€” often called parameterized properties.


Real-World Analogy โ€” The Apartment Building ๐Ÿข

Imagine a class called Apartment that manages tenants. Each apartment has multiple flats, and each flat has a tenant name. Instead of creating separate methods like GetTenant(int flatNumber) or SetTenant(int flatNumber, string name), you can use indexers for more natural syntax โ€” just like accessing a room by its number.

Example

public class Apartment
{
    private Dictionary<int, string> _tenants = new();

    public string this[int flatNumber]
    {
        get
        {
            if (_tenants.ContainsKey(flatNumber))
                return _tenants[flatNumber];
            return "Vacant";
        }
        set
        {
            _tenants[flatNumber] = value;
        }
    }
}

class Program
{
    static void Main()
    {
        var building = new Apartment();
        building[101] = "Ravi";
        building[102] = "Priya";

        Console.WriteLine($"Flat 101: {building[101]}");
        Console.WriteLine($"Flat 103: {building[103]}");
    }
}

This feels intuitive and human-readable โ€” youโ€™re literally asking, โ€œWho stays in flat 101?โ€ and the object responds accordingly.

Thatโ€™s what indexers do โ€” they turn your objects into self-contained mini-dictionaries that communicate in a natural way.


String-Based Indexers

Indexers arenโ€™t limited to numeric indexes. You can use strings or even custom objects as keys. Letโ€™s extend our apartment example โ€” now tenants can also be accessed by their names.

Example

public class PhoneDirectory
{
    private Dictionary<string, string> _contacts = new();

    public string this[string name]
    {
        get
        {
            if (_contacts.TryGetValue(name, out var number))
                return number;
            return "Contact not found";
        }
        set
        {
            _contacts[name] = value;
        }
    }
}

class Program
{
    static void Main()
    {
        var directory = new PhoneDirectory();
        directory["Bhargavi"] = "9876543210";
        directory["Kiran"] = "9123456780";

        Console.WriteLine($"Bhargavi's number: {directory["Bhargavi"]}");
        Console.WriteLine($"Ramesh's number: {directory["Ramesh"]}");
    }
}

This is much more expressive than calling GetContact("Bhargavi"). Indexers make your code readable and fluent โ€” ideal for collections, configurations, or entity lookups.


Real-Time Enterprise Use Case โ€” Configuration Access

Indexers are extremely useful in real projects. Imagine your application loads configuration values from a file or database โ€” like connection strings, API keys, or feature flags. With indexers, your ConfigManager class can behave like a dictionary, simplifying access everywhere in the codebase.

Example

public class ConfigManager
{
    private Dictionary<string, string> _settings = new()
    {
        { "AppName", "DotNet Full Stack Dev" },
        { "Theme", "Dark" },
        { "MaxUsers", "100" }
    };

    public string this[string key]
    {
        get => _settings.ContainsKey(key) ? _settings[key] : "Setting not found";
        set => _settings[key] = value;
    }
}

class Program
{
    static void Main()
    {
        var config = new ConfigManager();
        Console.WriteLine($"App Name: {config["AppName"]}");
        Console.WriteLine($"Theme: {config["Theme"]}");
    }
}

This makes your configuration access look clean and expressive โ€” instead of config.GetValue("AppName"), you simply write config["AppName"].


Multiple Indexers (Overloading)

You can even overload indexers by using different parameter types. This gives you multiple ways to access your data โ€” like by ID or by name.

Example

public class EmployeeDirectory
{
    private Dictionary<int, string> _byId = new();
    private Dictionary<string, int> _byName = new();

    public string this[int id]
    {
        get => _byId.ContainsKey(id) ? _byId[id] : "Employee not found";
        set => _byId[id] = value;
    }

    public int this[string name]
    {
        get => _byName.ContainsKey(name) ? _byName[name] : -1;
        set => _byName[name] = value;
    }
}

This flexibility allows indexers to adapt to your data design โ€” whether itโ€™s key-based, numeric, or text-based access.


Key Takeaways

  • Indexers make your classes feel like built-in collections.
  • They improve readability by replacing method calls with intuitive syntax.
  • You can define indexers with any data type as the key.
  • Ideal for dictionaries, configurations, caches, entity lookups, and domain collections.

Wrapping Up

Indexers in C# bring elegance and simplicity to class design. They let your objects communicate in a natural, expressive way โ€” much like accessing an array or dictionary, but wrapped in your own logic. From configuration managers to directories, from cache layers to entity collections, indexers make code cleaner and more intuitive without sacrificing control or performance.

Top comments (0)