总目录
前言
在 C# 中,数组和集合可以通过索引直接访问元素,例如 arr[0]
。但如果想让自定义的类或结构体也支持这种直观的访问方式,该怎么办?这时,索引器(Indexer) 就派上用场了!索引器是 C# 中一个强大但常被忽视的特性,它能为你的类型赋予类似数组的访问能力,同时隐藏底层数据结构的复杂性。本文将深入探讨索引器的语法、应用场景及高级技巧,并通过大量示例代码帮助你彻底掌握这一特性。
一、索引器是什么?
1. 基本概念
- 索引器允许类或结构的实例就像数组一样进行索引。 无需显式指定类型或实例成员,即可设置或检索索引值。 索引器类似于属性,不同之处在于它们的访问器需要使用参数。
- 索引器是一种特殊成员,允许类或结构体通过索引(如
obj[0]
)访问其内部数据。它的核心作用是为类型提供类似数组或字典的访问接口,从而简化代码逻辑,提升可读性。
2. 索引器与属性的区别
- 属性(Property):通过名称访问,例如
obj.Name
。 - 索引器(Indexer):通过索引(参数)访问,例如
obj[0]
或obj["key"]
。
索引器支持多参数,而属性没有参数。
3. 索引器有什么用?
在C#开发中,我们经常需要操作包含集合数据的自定义类型。传统方法通过Get/Set方法访问元素不仅繁琐,还破坏了代码的简洁性。索引器(Indexer)正是为解决这个问题而生,它允许我们使用类似数组的语法([]
)来访问对象内部集合,极大提升了代码的可读性和易用性。
二、索引器的使用
1. 索引器的定义
索引器使用 this
关键字定义,语法如下:
public 返回类型 this[参数类型 参数]
{
get { /* 获取逻辑 */ }
set { /* 设置逻辑 */ }
}
2. 基本操作
示例 1:普通对象中的使用
public class Person
{
private string _name, _pwd, _phone;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Pwd
{
get { return _pwd; }
set { _pwd = value; }
}
public string Phone
{
get { return _phone; }
set { _phone = value; }
}
//通过this关键字定义索引器,this代表的就是当前对象的实例,[]内就是索引器的参数列表
public string this[int index]
{
get
{
if (index == 0) return _name;
else if (index == 1) return _pwd;
else if (index == 2) return _phone;
else return null;
}
set
{
if (index == 0) _name = value;
else if (index == 1) _pwd = value;
else if (index == 2) _phone = value;
}
}
}
static void Main(string[] args)
{
//使用实例的属性赋值和取值
Person p = new Person();
p.Name = "AAA";
p.Pwd = "111";
p.Phone = "11111111111";
Console.WriteLine($"属性取值:Name={p.Name} Pwd={p.Pwd} Phone={p.Phone}");
//使用实例的索引器赋值和取值
Person person = new Person();
person[0] = "BBB";
person[1] = "222";
person[2] = "222222222";
Console.WriteLine($"索引器取值:Name={person[0]} Pwd={person[1]} Phone={person[2]}");
Console.ReadLine();
}
上面的案例通过定义一个索引器,来索引类中的每一个属性,使得我们可以使用类似于 person[0]的方式访问类中每一个属性并进行赋值和取值。
示例 2:封装数组的索引器
public class StringArray
{
private string[] _array = new string[10];
public string this[int index]
{
get => _array[index];
set => _array[index] = value;
}
}
// 使用示例
var arr = new StringArray();
arr[0] = "Hello"; // 调用 set 访问器
Console.WriteLine(arr[0]); // 输出 "Hello"
示例 3:多维索引器
索引器的参数可以是 int
、string
或其他类型(支持任意类型的参数),甚至支持多参数(多维索引器)。
public class Grid
{
private int[,] _grid = new int[10, 10];
public int this[int row, int col]
{
get => _grid[row, col];
set => _grid[row, col] = value;
}
}
// 使用示例
var grid = new Grid();
grid[3, 4] = 100; // 设置二维索引的值
3. 进阶操作
1) 读写控制
索引器可以仅暴露 get
或 set
,实现只读或只写:
public class ReadOnlyList
{
private int[] _data = { 1, 2, 3 };
// 只读索引器(省略 set)
public int this[int index] => _data[index];
}
2) 重载索引器
一个类可以定义多个索引器,通过参数类型或数量区分:
public class MultiIndexer
{
private List<string> _names = new List<string> { "Alice", "Bob" };
// 通过 int 索引获取名字
public string this[int index] => _names[index];
// 通过名字查找索引
public int this[string name] => _names.IndexOf(name);
}
// 使用示例
var indexer = new MultiIndexer();
Console.WriteLine(indexer[0]); // 输出 "Alice"
Console.WriteLine(indexer["Bob"]); // 输出 1
3) 动态逻辑处理
在 get
或 set
中添加自定义逻辑(如数据验证):
public class SafeArray
{
private int[] _array = new int[10];
public int this[int index]
{
get
{
if (index < 0 || index >= _array.Length)
throw new IndexOutOfRangeException();
return _array[index];
}
set
{
if (index < 0 || index >= _array.Length)
throw new IndexOutOfRangeException();
_array[index] = value;
}
}
}
4) 接口中的索引器
索引器可以在接口中定义,由实现类具体实现:
public interface IArrayContainer
{
int this[int index] { get; set; }
}
public class MyArray : IArrayContainer
{
private int[] _array = new int[10];
public int this[int index]
{
get => _array[index];
set => _array[index] = value;
}
}
四、索引器的应用场景
1. 封装集合类
自定义集合类时,索引器可提供类似原生数组的访问方式:
public class CustomList<T>
{
private T[] _items = new T[10];
private int _count = 0;
public T this[int index]
{
get => _items[index];
set => _items[index] = value;
}
public void Add(T item) => _items[_count++] = item;
}
2. 多维数据或复杂结构 访问
例如矩阵、网格或表格数据的封装。以下示例是封装温度数据,通过日期索引访问:
public class TemperatureData
{
private float[] _temperatures = new float[365];
public float this[DateTime date]
{
get => _temperatures[date.DayOfYear - 1];
set => _temperatures[date.DayOfYear - 1] = value;
}
}
// 使用示例
var tempData = new TemperatureData();
tempData[new DateTime(2023, 7, 1)] = 35.5f;
3. 替代冗余方法
简化类似 GetValue(int index)
和 SetValue(int index, T value)
的方法调用。
// 没有索引器的写法
var value = list.GetValue(0);
list.SetValue(0, "New Value");
// 使用索引器的写法
var value = list[0];
list[0] = "New Value";
五、注意事项
命名规则
索引器只能命名为this
,不能自定义名称。静态索引器
C# 不支持静态索引器(如public static int this[int index]
)。性能优化
避免在索引器中实现复杂逻辑(如数据库查询),尤其是在高频访问场景中。异常处理
始终验证索引的有效性,避免抛出未处理的IndexOutOfRangeException
。索引器与属性的区别
- 属性通过名称访问(如
obj.Name
),索引器通过索引访问(如obj[0]
)。 - 索引器可以有多个参数,属性没有参数。
- 属性通过名称访问(如
合理使用索引器可以让代码更优雅,但需注意避免过度设计。如果你正在开发自定义集合或需要为类型提供直观的访问接口,索引器将是一个得力的工具!
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
C# 官方文档 - 索引器