C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
26: Indexers
I've noticed a recurring pattern when developers first encounter indexers: they assume that adding an indexer to a class somehow transforms that class into a collection or an array. They think the indexer is the storage mechanism. It's a subtle but important mistake.
If you think an indexer creates a list, you'll be confused the moment you see a class that provides an indexer but doesn't actually hold a list internally. For example, imagine a class that fetches data from a database or a remote API on the fly. It might look like an array to the user of the class, but inside, it's just making network calls. The indexer is the interface, not the implementation.
Indexers aren't collections; they are parameterized properties
The best way to think about an indexer is that it's a property that takes an argument. You already know how a standard property works: you ask for myObject.Name and it gives you a value. An indexer lets you ask for myObject[index]. That [index] is simply a parameter being passed to a get or set accessor.
Here is a concrete example. Let's build a Playlist class. We'll use a private list to hold the songs, but we'll use an indexer to control how those songs are accessed and modified.
public class Playlist
{
private List<string> _songs = new List<string>();
// This is the indexer
public string this[int index]
{
get
{
// I can add custom logic here, like bounds checking
// or logging, which a raw list wouldn't do.
if (index < 0 || index >= _songs.Count)
throw new IndexOutOfRangeException("That song isn't in the playlist!");
return _songs[index];
}
set
{
_songs[index] = value;
}
}
public void AddSong(string song) => _songs.Add(song);
public int Count => _songs.Count;
}
When you use playlist[0] = "Bohemian Rhapsody", you aren't calling a method in the traditional sense, but you are executing the set block of the indexer. The this keyword is what tells C# "this property belongs to the object instance itself, not a named field."
You aren't limited to integers
This is where indexers get really powerful. Since the index is just a parameter, it doesn't have to be an int. You can use a string, a Guid, or even a custom object as the key. This allows you to create a class that feels like a dictionary but behaves exactly how you want it to.
I often use this for configuration managers. Instead of passing a key into a GetSetting(string key) method, it feels much cleaner to just index into the manager.
public class AppSettings
{
private Dictionary<string, string> _settings = new Dictionary<string, string>();
public string this[string key]
{
get
{
if (_settings.ContainsKey(key))
return _settings[key];
return "Setting not found"; // Returning a default instead of crashing
}
set
{
_settings[key] = value;
}
}
}
// Usage:
var config = new AppSettings();
config["Theme"] = "Dark Mode";
Console.WriteLine(config["Theme"]); // Output: Dark Mode
Console.WriteLine(config["FontSize"]); // Output: Setting not found
Notice how the AppSettings class doesn't inherit from Dictionary. It contains one. By using an indexer, I've hidden the dictionary implementation and provided a safe way to access the data without worrying about KeyNotFoundException.
📋 Practical Task
Build a Custom TranslationTable
Your task is to create a class called TranslationTable that allows a user to map English words to their translations in another language using an indexer.
- The class should use a
Dictionary<string, string>internally to store the words. - Implement a string-based indexer.
- The
getaccessor should return the translated word. If the word does not exist in the dictionary, it should return the string"[No Translation]". - The
setaccessor should add or update the translation for the given English word.
Test your implementation with the following logic:
var table = new TranslationTable();
table["Apple"] = "Manzana";
table["Book"] = "Libro";
Console.WriteLine(table["Apple"]); // Should print: Manzana
Console.WriteLine(table["Cloud"]); // Should print: [No Translation]
There are no comments for now.