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?
Declaration
public dataType this[int index]
{
get { }
set { }
}
Key Features of Indexers
Access Modifiers: You can specify different access levels for get and set. For example, get can be public, and set can be protected.
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;
Console.WriteLine(collection[0]);
}
}
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 { }
set { }
}
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]);
Console.WriteLine(matrix[1, 1]);
}
}
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);
if (index != -1) stringArray[index] = value;
}
}
}
class Program
{
static void Main()
{
var obj = new OverloadedIndexers();
obj[0] = 42;
obj["First"] = "Hello";
Console.WriteLine(obj[0]);
Console.WriteLine(obj["First"]);
}
}
Properties
1. Introduction
Properties encapsulate fields and provide logic while getting or setting their values.
Declaration:
public dataType PropertyName
{
get { }
set { }
}
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 get and set.
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]}");
}
}