博客 / 詳情

返回

C#.NET 索引器完全解析:語法、場景與最佳實踐

簡介

索引器(Indexer)是 C# 中的一種特殊屬性,它允許類或結構體像數組一樣使用索引語法(例如 obj[0])來訪問或修改對象內部的成員。簡單來説,它將對象的實例視為“可索引的集合”,提供類似於數組的訪問方式。

  • 核心特性:

    • 類似於屬性(Property),但帶有參數(通常是索引值,如整數或字符串)。
    • 支持 getset 訪問器,與屬性類似。
    • 可以重載(overload),允許不同類型的索引參數。
    • 語法:public 類型 this[參數類型 參數名] { get { ... } set { ... } }
  • 適用範圍:類、結構體、接口。不能在委託或枚舉中使用。

索引器本質上是方法(get/set),但編譯器將其轉換為特殊的屬性調用,隱藏了底層實現細節。

語法結構:

public <返回類型> this[<參數類型> index]
{
    get { ... }
    set { ... }
}
  • this[...] 表示作用於當前對象實例。
  • 可以有多個索引參數(如二維索引)。

為什麼使用索引器?

在面向對象編程中,直接暴露內部數組或集合(如 public int[] Data { get; })會破壞封裝性(客户端可隨意修改)。索引器提供受控訪問:

  • 封裝性:隱藏內部存儲(如 List、Dictionary),允許驗證、轉換或緩存邏輯。
  • 直觀性:代碼更像數組操作,提升可讀性(e.g., cache["key"] = value; 而非 cache.Set("key", value);)。
  • 適用場景:

    • 模擬數組/集合(如自定義列表、矩陣)。
    • 字典式訪問(字符串鍵)。
    • 多維數據(如圖像像素訪問)。
    • LINQforeach 集成(需實現 IEnumerable)。

相比普通方法,索引器更簡潔;相比屬性,它支持參數化訪問。

基本示例

public class MyList
{
    private string[] _data = new string[5];

    public string this[int index]
    {
        get => _data[index];
        set => _data[index] = value;
    }
}

使用方式:

var list = new MyList();
list[0] = "Hello";
list[1] = "World";

Console.WriteLine(list[0]); // 輸出:Hello
Console.WriteLine(list[1]); // 輸出:World

看起來就像數組訪問,但其實內部是屬性訪問。

索引器與屬性的關係

對比項 屬性 (Property) 索引器 (Indexer)
名稱 有名字 名為 this
參數 無參數 有一個或多個參數
調用方式 obj.Property obj[index]
典型用途 訪問字段值 訪問集合或映射內容

索引器的底層機制

編譯後:

obj[index]

其實會被編譯為:

obj.get_Item(index);    // 讀取
obj.set_Item(index, x); // 賦值

也就是説,索引器就是名為 Item 的一對 get/set 方法(CLR 級別)。

不同類型的索引器

整數索引器

public class IntArrayWrapper
{
    private int[] _array;

    public IntArrayWrapper(int size)
    {
        _array = new int[size];
    }

    public int this[int index]
    {
        get => _array[index];
        set => _array[index] = value;
    }

    public int Length => _array.Length;
}

// 使用
var wrapper = new IntArrayWrapper(5);
wrapper[0] = 10;
wrapper[1] = 20;
Console.WriteLine(wrapper[0]); // 輸出: 10

字符串索引器

public class DictionaryWrapper
{
    private Dictionary<string, string> _dictionary = new Dictionary<string, string>();

    public string this[string key]
    {
        get
        {
            _dictionary.TryGetValue(key, out string value);
            return value;
        }
        set
        {
            _dictionary[key] = value;
        }
    }
}

// 使用
var dictWrapper = new DictionaryWrapper();
dictWrapper["name"] = "John";
dictWrapper["age"] = "30";

Console.WriteLine(dictWrapper["name"]); // 輸出: John

多參數索引器

public class Matrix
{
    private double[,] _matrix;

    public Matrix(int rows, int columns)
    {
        _matrix = new double[rows, columns];
    }

    // 多參數索引器
    public double this[int row, int column]
    {
        get => _matrix[row, column];
        set => _matrix[row, column] = value;
    }

    public int Rows => _matrix.GetLength(0);
    public int Columns => _matrix.GetLength(1);
}

// 使用
var matrix = new Matrix(3, 3);
matrix[0, 0] = 1.0;
matrix[1, 1] = 2.0;
matrix[2, 2] = 3.0;

Console.WriteLine(matrix[1, 1]); // 輸出: 2.0

重載索引器

public class MultiIndexCollection
{
    private List<string> _items = new List<string>();

    public void Add(string item) => _items.Add(item);

    // 整數索引器
    public string this[int index]
    {
        get => _items[index];
        set => _items[index] = value;
    }

    // 字符串索引器 - 通過名稱查找
    public string this[string name]
    {
        get => _items.Find(item => item.StartsWith(name));
    }
}

// 使用
var collection = new MultiIndexCollection();
collection.Add("Apple");
collection.Add("Banana");
collection.Add("Cherry");

Console.WriteLine(collection[0]);    // 輸出: Apple
Console.WriteLine(collection["B"]);  // 輸出: Banana

高級用法

只讀索引器

public class Settings
{
    private readonly Dictionary<string, string> _values = new();

    public string this[string key]
    {
        get => _values.TryGetValue(key, out var value) ? value : string.Empty;
        set => _values[key] = value;
    }
}

使用:

var s = new Settings();
s["Language"] = "Chinese";
s["Theme"] = "Dark";

Console.WriteLine(s["Language"]); // Chinese

只寫:

public string this[int index]
{
    set => _data[index] = value;
}

索引器可以被繼承或重寫

父類定義:

public class Base
{
    public virtual string this[int index]
    {
        get => $"Base:{index}";
        set => Console.WriteLine($"Set Base[{index}]={value}");
    }
}

子類重寫:

public class Derived : Base
{
    public override string this[int index]
    {
        get => $"Derived:{index}";
        set => Console.WriteLine($"Set Derived[{index}]={value}");
    }
}

接口中的索引器

public interface IListContainer<T>
{
    T this[int index] { get; set; }
    int Count { get; }
}

public class MyList<T> : IListContainer<T>
{
    private List<T> _items = new List<T>();

    public T this[int index]
    {
        get => _items[index];
        set => _items[index] = value;
    }

    public int Count => _items.Count;

    public void Add(T item) => _items.Add(item);
}

實際應用示例

配置管理器

public class Configuration
{
    private readonly Dictionary<string, object> _settings = new Dictionary<string, object>();

    public object this[string key]
    {
        get => _settings.TryGetValue(key, out object value) ? value : null;
        set => _settings[key] = value;
    }

    public T Get<T>(string key, T defaultValue = default)
    {
        if (_settings.TryGetValue(key, out object value) && value is T typedValue)
        {
            return typedValue;
        }
        return defaultValue;
    }
}

// 使用
var config = new Configuration();
config["DatabaseConnection"] = "Server=localhost;Database=Test;";
config["Timeout"] = 30;

string connection = config.Get<string>("DatabaseConnection");
int timeout = config.Get<int>("Timeout");

自定義集合類

public class SmartCollection<T>
{
    private T[] _items;
    
    public SmartCollection(int size) => _items = new T[size];
    
    public T this[int index]
    {
        get => _items[index];
        set => _items[index] = value;
    }
    
    // 重載索引器
    public T this[string name] => FindByName(name);
    
    private T FindByName(string name)
    {
        // 根據名稱查找邏輯...
    }
}

數據訪問層封裝

public class DataRepository
{
    private List<Customer> _customers = new();
    
    public Customer this[int id]
    {
        get => _customers.FirstOrDefault(c => c.Id == id);
    }
    
    public Customer this[string email]
    {
        get => _customers.FirstOrDefault(c => c.Email == email);
    }
}

索引器與其他特性結合

索引器與泛型

public class GenericCollection<T>
{
    private T[] _items = new T[10];
    
    public T this[int index]
    {
        get => _items[index];
        set => _items[index] = value;
    }
}

索引器與模式匹配(C# 8.0+)

if (collection is IIndexable<int, string> indexable)
{
    Console.WriteLine(indexable[0]);
}

索引器與範圍支持(C# 8.0+)

public class RangeCollection
{
    private int[] _items = {1, 2, 3, 4, 5};
    
    public int[] this[Range range]
    {
        get => _items[range];
    }
}

// 使用
var collection = new RangeCollection();
int[] sub = collection[1..4]; // [2, 3, 4]

常見應用場景

場景 示例
模擬集合/字典訪問 myDict[key]
操作二維數據 matrix[row, col]
管理配置項 settings["Theme"]
實現對象簡潔訪問接口 student["Name"]api["token"]
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.