Indexer cho phép instance của class/struct được truy cập như mảng: obj[index]. Được khai báo với từ khóa this.
Cú pháp cơ bản
public class MyClass
{
private string[] _data = new string[10];
// Indexer với 1 tham số int
public string this[int index]
{
get => _data[index];
set => _data[index] = value;
}
}
Ví dụ 1: Indexer đơn giản (int index)
public class Team
{
private string[] _members = new string[10];
public string this[int index]
{
get => _members[index];
set => _members[index] = value;
}
}
class Program
{
static void Main()
{
Team team = new Team();
team[0] = "Alice";
team[1] = "Bob";
team[2] = "Charlie";
for (int i = 0; i < 3; i++)
Console.WriteLine(team[i]); // Alice, Bob, Charlie
Console.ReadLine();
}
}
Ví dụ 2: Overload Indexer (Nhiều tham số, kiểu khác)
public class Matrix
{
private int[,] _data = new int[3, 3];
// Indexer 2 chiều
public int this[int row, int col]
{
get => _data[row, col];
set => _data[row, col] = value;
}
// Indexer string (tìm kiếm theo tên)
private Dictionary _lookup = new();
public int this[string name]
{
get => _lookup.TryGetValue(name, out int val) ? val : -1;
set => _lookup[name] = value;
}
}
class Program
{
static void Main()
{
Matrix m = new Matrix();
m[0, 1] = 42;
m["score"] = 100;
Console.WriteLine(m[0, 1]); // 42
Console.WriteLine(m["score"]); // 100
Console.WriteLine(m["missing"]); // -1
}
}
Ví dụ 3: Read-only Indexer (Chỉ get)
public class ReadOnlyList
{
private readonly List _items = new();
public ReadOnlyList(params string[] items) => _items.AddRange(items);
// Chỉ get
public string this[int index] => _items[index];
public int Count => _items.Count;
}
Indexer trong Interface
public interface IRepository
{
T this[int id] { get; set; }
IEnumerable GetAll();
}
public class UserRepository : IRepository
{
private Dictionary _users = new();
public User this[int id]
{
get => _users.TryGetValue(id, out var u) ? u : null;
set => _users[id] = value;
}
// ...
}
Best Practices
- Indexer nên có logic validation (bounds check, null check).
- Tránh side effects phức tạp trong getter/setter.
- Có thể overload nhiều indexer với signature khác nhau.
- Interface thường khai báo indexer để định nghĩa contract.
- Tên tham số indexer nên có ý nghĩa (vd:
index,key,id).
Expression-bodied Indexer (C# 7+)
public string this[int i] => _data[i]; // Read-only
public string this[int i] { get => _data[i]; set => _data[i] = value; } // Read-write