C#核心学习(五)面向对象--封装(4)C#中的索引器详解:让对象像数组一样灵活访问
目录
一、C#中的索引器是什么?
二、索引器有什么作用?
1. 增强封装性
2. 提升代码可读性
3. 支持复杂逻辑
三、怎么进行申明
基础语法
重载索引器
四、总结
核心价值
适用场景
注意事项
1. 数组(Array)
2. 字符串(string)
3. **List**
4. **Dictionary**
5. **DataRow(ADO.NET)**
6. **SortedList**
7. 自定义类型(类/结构体)
注意事项
在C#编程中,你是否遇到过这样的场景:封装了一个复杂的数据结构,却希望它能像数组一样通过[]
符号被轻松访问?或者想要为自定义类型赋予类似List
或Dictionary
的直观元素操作体验?
索引器(Indexer)正是为此而生。它不仅是C#语法中的一颗“语法糖”,更是一种提升代码表现力的利器。通过索引器,开发者可以隐藏底层数据结构的复杂性,以一致、简洁的语法对外暴露数据访问逻辑。无论是遍历多维矩阵、快速查询键值对,还是为业务模型定制特殊的数据规则,索引器都能让代码变得更加优雅且易于维护。
本文将从核心概念、实际作用到实现细节,带你全面掌握C#索引器的使用技巧,并通过常见数据类型(如数组、字典、DataRow
等)的案例,揭示其灵活性与实用性。无论你是刚接触索引器的新手,还是希望深化理解的开发者,这里都有你需要的答案。
一、C#中的索引器是什么?
索引器(Indexer)是C#中一种特殊的成员,允许对象像数组一样通过索引值进行数据访问。它本质上是对对象下标操作符([])的重载,使得开发者可以为自定义类型(如类或结构体)定义类似数组的访问行为。索引器的核心特点是用参数化的属性语法封装内部数据,从而提供更直观的数据访问方式。
例如,当我们需要像访问数组元素一样访问一个自定义集合对象的元素时,可以通过索引器实现:
MyCollection collection = new MyCollection();
Console.WriteLine(collection[0]); // 通过索引器访问
二、索引器有什么作用?
1. 增强封装性
通过索引器隐藏内部数据结构(如数组、字典等)的实现细节,对外提供统一的访问接口。例如,一个封装数据库查询结果的类可以通过索引器暴露数据行:
public class QueryResult {
private DataRow[] _rows;
public DataRow this[int index] => _rows[index];
}
2. 提升代码可读性
使自定义类型的元素访问语法与内置类型(如数组、列表)一致,降低学习成本:
// 传统方法 vs 索引器
var value1 = myObj.GetValueAt(5); // 方法调用
var value2 = myObj[5]; // 索引器调用
3. 支持复杂逻辑
可以在get
或set
访问器中添加验证、计算、缓存等逻辑:
public class TemperatureData {
private float[] _temps;
public float this[int day] {
get { return _temps[day % 7]; } // 实现循环访问
set { _temps[day % 7] = value; }
}
}
三、怎么进行申明
基础语法
public class MyClass {
// 声明索引器
// 返回类型 参数类型 索引
public ReturnType this[ParameterType index] {
get { /* 返回值逻辑 */ }
set { /* 赋值逻辑 */ }
}
}
示例:二维索引器
public class Matrix {
private int[,] _matrix = new int[10,10];
public int this[int row, int col] {
get => _matrix[row, col];
set => _matrix[row, col] = value;
}
}
// 使用
Matrix m = new Matrix();
m[3,5] = 100;
重载索引器
支持通过不同参数类型或数量进行重载:
public class MultiIndexer {
// 整数索引
public string this[int index] { ... }
// 字符串索引
public string this[string key] { ... }
// 多参数索引
public int this[int x, int y] { ... }
}
四、总结
核心价值
- 使自定义类型拥有类似数组的访问语法
- 增强代码的可维护性和扩展性
- 支持参数化数据访问逻辑
适用场景
- 封装集合类(如自定义List/Dictionary)
- 多维数据访问(如矩阵、表格)
- 需要验证或处理访问逻辑的场合
注意事项
- 避免过度使用导致代码可读性下降
- 优先考虑使用现有集合类型(如
List<T>
)
其实在前面我们就已经学到过一些索引器了,接下来我举几个看看:
1. 数组(Array)
所有C#数组(一维、多维、交错数组)均内置索引器,通过整型索引访问元素:
// 一维数组
int[] numbers = { 10, 20, 30 };
Console.WriteLine(numbers[0]); // 输出 10
// 二维数组
int[,] matrix = new int[2, 2] { { 1, 2 }, { 3, 4 } };
Console.WriteLine(matrix[1, 0]); // 输出 3
// 交错数组(数组的数组)
int[][] jagged = { new int[] { 1 }, new int[] { 2, 3 } };
Console.WriteLine(jagged[1][0]); // 输出 2
2. 字符串(string)
字符串通过索引器直接访问字符(只读,因字符串不可变):
string text = "Hello";
char firstChar = text[0]; // 'H'
// text[0] = 'h'; // 错误!字符串不可修改
3. **List<T>
**
泛型列表通过整型索引访问元素,支持动态扩容:
List<string> names = new List<string> { "Alice", "Bob" };
Console.WriteLine(names[1]); // 输出 "Bob"
names[0] = "Alex"; // 修改元素
4. **Dictionary<TKey, TValue>
**
字典通过键(任意类型)作为索引参数访问值:
Dictionary<string, int> ages = new Dictionary<string, int>();
ages["Alice"] = 25;
Console.WriteLine(ages["Alice"]); // 输出 25
5. **DataRow
(ADO.NET)**
DataRow
通过列名或列索引访问单元格数据:
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Rows.Add("Alice");
DataRow row = table.Rows[0];
string name1 = row["Name"]; // 列名索引
string name2 = row[0]; // 列索引(整型)
6. **SortedList<TKey, TValue>
**
有序列表同时支持通过键和整型索引访问元素:
SortedList<int, string> list = new SortedList<int, string>();
list.Add(3, "Three");
list.Add(1, "One");
Console.WriteLine(list[3]); // 键索引 → "Three"
Console.WriteLine(list.Values[0]); // 整型索引 → "One"(按排序顺序)
7. 自定义类型(类/结构体)
自定义类型声明索引器,支持任意参数类型和逻辑:
public class ColorPalette {
private string[] _colors = { "Red", "Green", "Blue" };
// 整型索引器
public string this[int index] {
get => _colors[index];
set => _colors[index] = value;
}
// 字符串索引器(通过名称获取颜色)
public int this[string name] {
get => Array.IndexOf(_colors, name);
}
}
// 使用
ColorPalette palette = new ColorPalette();
Console.WriteLine(palette[0]); // 输出 "Red"
Console.WriteLine(palette["Blue"]); // 输出 2
数据类型 | 索引器参数类型 | 返回值类型 | 特点 |
---|---|---|---|
数组(int[] ) | int | 元素类型(如 int ) | 内置,多维索引 |
字符串(string ) | int | char | 只读 |
List<T> | int | T | 动态扩容 |
Dictionary<TKey,TValue> | TKey | TValue | 键查找高效 |
DataRow | int 或 string | object | 支持列名和列索引 |
自定义类 | 任意类型(可多参数) | 任意类型 | 可封装复杂逻辑 |
注意事项
- 内置索引器不可修改:如数组、字符串的索引器是系统实现的,无法自定义。
- 索引越界:大多数索引器在访问无效索引时会抛出
IndexOutOfRangeException
或KeyNotFoundException
(字典)。- 性能:字典的索引器(通过键查找)时间复杂度为 O(1),而线性查找的索引器(如
List.IndexOf
)为 O(n)。