Mastering C# Part 3.3 Indexers & Properties
Complete Indexers & Properties concept with examples

Indexers
Indexers allow instances of a class or struct to be accessed using array-like syntax. They provide a way to create classes that work as collections.
Why Use Indexers?
To encapsulate data structures like arrays or lists.
To provide custom access logic for accessing elements.
Declaration
public dataType this[int index]
{
get { /* logic for returning value */ }
set { /* logic for assigning value */ }
}
Key Features of Indexers
Access Modifiers: You can specify different access levels for
getandset. For example,getcan bepublic, andsetcan beprotected.Custom Logic: You can include validation or transformation logic within indexer accessors.
No Static Indexers: Indexers cannot be static.
Advanced Example
Here’s an example demonstrating validation and custom logic:
using System;
class CustomCollection
{
private int[] _data = new int[5];
public int this[int index]
{
get
{
if (index < 0 || index >= _data.Length)
throw new IndexOutOfRangeException("Invalid index");
return _data[index];
}
set
{
if (index < 0 || index >= _data.Length)
throw new IndexOutOfRangeException("Invalid index");
if (value < 0)
throw new ArgumentException("Value cannot be negative");
_data[index] = value;
}
}
}
class Program
{
static void Main()
{
var collection = new CustomCollection();
collection[0] = 42; // Valid
Console.WriteLine(collection[0]); // Output: 42
// collection[5] = 10; // Throws IndexOutOfRangeException
}
}
Multidimensional Indexers
Multidimensional indexers allow classes to act as multidimensional arrays.
Declaration
Multidimensional indexers require multiple parameters:
public dataType this[int index1, int index2]
{
get { /* logic */ }
set { /* logic */ }
}
Example: Managing a Matrix
using System;
class Matrix
{
private int[,] _matrix;
public Matrix(int rows, int cols)
{
_matrix = new int[rows, cols];
}
public int this[int row, int col]
{
get
{
if (row < 0 || row >= _matrix.GetLength(0) || col < 0 || col >= _matrix.GetLength(1))
throw new IndexOutOfRangeException("Invalid indices");
return _matrix[row, col];
}
set
{
if (row < 0 || row >= _matrix.GetLength(0) || col < 0 || col >= _matrix.GetLength(1))
throw new IndexOutOfRangeException("Invalid indices");
_matrix[row, col] = value;
}
}
}
class Program
{
static void Main()
{
var matrix = new Matrix(3, 3);
matrix[0, 0] = 5;
matrix[1, 1] = 10;
Console.WriteLine(matrix[0, 0]); // Output: 5
Console.WriteLine(matrix[1, 1]); // Output: 10
}
}
Overloading Indexers
Concept
You can define multiple indexers in a single class by overloading them with different parameter types or counts.
Example: Overloading Based on Parameter Type
using System;
class OverloadedIndexers
{
private int[] intArray = new int[5];
private string[] stringArray = new string[5];
public int this[int index]
{
get => intArray[index];
set => intArray[index] = value;
}
public string this[string key]
{
get
{
int index = Array.IndexOf(stringArray, key);
return index != -1 ? stringArray[index] : null;
}
set
{
int index = Array.IndexOf(stringArray, null); // Find empty slot
if (index != -1) stringArray[index] = value;
}
}
}
class Program
{
static void Main()
{
var obj = new OverloadedIndexers();
obj[0] = 42; // Integer index
obj["First"] = "Hello"; // String key
Console.WriteLine(obj[0]); // Output: 42
Console.WriteLine(obj["First"]); // Output: Hello
}
}
Properties
1. Introduction
Properties encapsulate fields and provide logic while getting or setting their values.
Declaration:
public dataType PropertyName
{
get { /* logic */ }
set { /* logic */ }
}
2. Advanced Features
a. Backing Fields
You can define a private field to store property values.
private int _value;
public int Value
{
get => _value;
set => _value = value;
}
b. Auto-Implemented Properties
Properties with no explicit backing field.
public int Value { get; set; }
c. Read-Only Properties
Use get only:
public int ReadOnlyValue { get; } = 42;
d. Computed Properties
Properties can compute their values dynamically:
public int Square => Value * Value;
Restrictions on Properties
Cannot Take Parameters: Unlike indexers, properties do not accept parameters.
Overloading Is Not Allowed: You cannot overload properties with the same name.
No Logic Outside Accessors: Properties cannot perform complex logic outside
getandset.Static and Instance Properties: Static properties belong to the class, while instance properties belong to objects.
Practical Example Combining Properties and Indexers
using System;
class Library
{
private string[] books = new string[10];
public string[] Books
{
get => books;
set => books = value;
}
public string this[int index]
{
get => books[index];
set => books[index] = value;
}
public int BookCount => books.Length;
}
class Program
{
static void Main()
{
var library = new Library();
library[0] = "C# in Depth";
library[1] = "Pro ASP.NET Core";
Console.WriteLine($"Total Books: {library.BookCount}");
Console.WriteLine($"First Book: {library[0]}");
}
}
