Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

วิธี Overload Indexer (Operator []) ใน C#

Indexer (เรียกว่า this[]) ช่วยให้ Object ใช้ Syntax แบบ Array ได้ obj[index] สามารถ Overload ได้หลาย Signature

Syntax พื้นฐาน

public class MyCollection
{
    private string[] _data = new string[10];
    
    // Indexer แบบ Integer
    public string this[int index]
    {
        get => _data[index];
        set => _data[index] = value;
    }
}

ตัวอย่างที่ 1: Basic Integer Indexer

public class Team
{
    private string[] _members = new string[10];
    
    public string this[int index]
    {
        get => _members[index];
        set => _members[index] = value;
    }
}

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
}

ตัวอย่างที่ 2: Overload Indexer (Multiple Parameters/Types)

public class Matrix
{
    private double[,] _data;
    
    public Matrix(int rows, int cols)
    {
        _data = new double[rows, cols];
    }
    
    // Indexer 2 Parameters (Row, Col)
    public double this[int row, int col]
    {
        get => _data[row, col];
        set => _data[row, col] = value;
    }
    
    // Indexer String (Named Access)
    public double this[string name]
    {
        get => name switch
        {
            "rows" => _data.GetLength(0),
            "cols" => _data.GetLength(1),
            _ => throw new ArgumentException("Invalid name")
        };
    }
}

static void Main()
{
    Matrix m = new Matrix(3, 4);
    m[0, 0] = 1.5;
    m[1, 2] = 2.5;
    
    Console.WriteLine(m[0, 0]);   // 1.5
    Console.WriteLine(m["rows"]); // 3
    Console.WriteLine(m["cols"]); // 4
}

ตัวอย่างที่ 3: Dictionary-like Indexer (String Key)

public class Settings
{
    private readonly Dictionary _values = new();
    
    public string this[string key]
    {
        get => _values.TryGetValue(key, out var v) ? v : null;
        set => _values[key] = value;
    }
    
    // Read-only Indexer (Expression-bodied)
    public int Count => _values.Count;
}

static void Main()
{
    Settings settings = new Settings();
    settings["theme"] = "dark";
    settings["lang"] = "th";
    
    Console.WriteLine(settings["theme"]); // dark
    Console.WriteLine(settings["lang"]);  // th
    Console.WriteLine(settings["missing"]); // null
}

ข้อจำกัดและกฎ

  • Indexer ต้องเป็น instance Member (ไม่ใช่ Static)
  • ชื่อต้องเป็น this เสมอ
  • ต้องมีอย่างน้อย 1 Parameter
  • Overload ได้โดย Parameter Types/Count ต่างกัน
  • ไม่สามารถใช้ ref, out, in Parameters
  • Interface สามารถประกาศ Indexer ได้

Interface Indexer

public interface IRepository
{
    T this[int id] { get; set; }
    T this[string key] { get; }
}

public class UserRepository : IRepository
{
    private Dictionary _byId = new();
    private Dictionary _byEmail = new();
    
    public User this[int id]
    {
        get => _byId[id];
        set => _byId[id] = value;
    }
    
    public User this[string email]
    {
        get => _byEmail.TryGetValue(email, out var u) ? u : null;
    }
}

Pattern Matching Indexer (C# 9+)

public class FlexibleIndexer
{
    private Dictionary _data = new();
    
    public object this[object key] => key switch
    {
        int i => _data[$"int:{i}"],
        string s => _data[s],
        Guid g => _data[$"guid:{g}"],
        _ => throw new ArgumentException("Unsupported key type")
    };
}

Best Practices

  • Validate Index/Key (Throw ArgumentOutOfRangeException หรือ KeyNotFoundException)
  • Implement IEnumerable<T> หากเป็น Collection
  • Consider TryGetValue Pattern สำหรับ Safe Access
  • Document Expected Key Types/Ranges