1. 引言
想象一下:你有个集合……你想快速拿到第三个、第七个,或者说第零个元素。或者想把它们换个位置。在数组里这很简单——直接用索引(array[3])。那集合呢?不是所有集合都能随便用索引!
这时候IList<T>接口就登场了——它是个通用契约,要求集合必须支持按索引操作元素。简单说:你有个实现了IList<T>的集合——你就可以像数组那样用索引访问和修改它的元素。
类比:
想想图书馆的卡片:每本书在书架上都有自己的编号,你随时可以去拿“第三本”书。支持IList<T>的集合就是这么干的。
2. IList<T>的整体结构和方法
IList<T>接口是很多集合的主角。它继承自ICollection<T>(而它又继承自IEnumerable<T>,这样你就能用循环遍历集合),并且加上了最重要的:索引操作。
接口继承结构图:
IEnumerable<T>
▲
│
ICollection<T>
▲
│
IList<T>
IList<T>接口的主要成员
| 成员 | 作用 |
|---|---|
|
按索引获取或设置元素 |
|
查找元素第一次出现的索引 |
|
在指定位置插入元素 |
|
按索引删除元素 |
其他成员,比如Add、Remove、Clear、Contains,都是从ICollection<T>接口来的。
核心亮点:索引器
IList<T>最酷的地方就是有索引器。这就是语法糖,让你可以这样写:
var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // 得到20
myList[2] = 42; // 改第三个元素
3. 哪些集合实现了IList<T>
在.NET标准库里,很多常见的数据结构都支持IList<T>接口。来看看最常用的几个:
| 集合 | 支持索引 | 说明 |
|---|---|---|
|
是 | 动态数组 |
|
是 | 普通数组支持索引 |
|
是 | 用于数据绑定 |
|
是 | 带通知的列表 |
|
是 | 集合的基类 |
注意:
LinkedList<T> 和
HashSet<T>
不实现
IList<T>,因为它们没有快速索引(对,
LinkedList<T>没有
list[5]!)。
4. IList<T>的用法示例
按索引获取和设置
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
IList<string> fruits = new List<string> { "苹果", "香蕉", "梨" };
// 获取第二个元素
string fruit = fruits[1];
Console.WriteLine(fruit); // 香蕉
// 替换第三个元素
fruits[2] = "橙子";
Console.WriteLine(fruits[2]); // 橙子
}
}
按索引插入和删除
fruits.Insert(1, "猕猴桃"); // 在第二个位置插入“猕猴桃”
// 列表现在是:"苹果", "猕猴桃", "香蕉", "橙子"
fruits.RemoveAt(0); // 删除第一个元素("苹果")
// 列表现在是:"猕猴桃", "香蕉", "橙子"
查找元素的索引
int index = fruits.IndexOf("橙子"); // 返回索引(2),找不到就-1
if (index != -1)
Console.WriteLine("橙子在位置:" + index);
else
Console.WriteLine("橙子没找到");
5. 实现细节和常见错误
用IList<T>时很容易踩坑,尤其是忘了索引从零开始,集合长度就是当前元素数。
比如,试图访问不存在的元素:
Console.WriteLine(fruits[100]); // IndexOutOfRangeException!
C#里的索引就像母鸡下蛋:从零开始,不是从一。如果列表有4个元素,最后一个索引是3。
还要记住,不是所有IList<T>实现都一样快。比如数组和List<T>按索引访问是瞬间的(O(1)),但如果你用链表实现自己的集合然后强行加上IList<T>,那操作可能会很慢。标准库不会这么干。
还有一点:如果你把数组当作IList<T>用,可以改元素,但不能改数组长度。对数组用Add、Remove、Insert这些方法会抛出NotSupportedException。
int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // 向上转型
listView[0] = 42; // 可以!
listView.Add(99); // 会抛出NotSupportedException
6. 实际应用和意义
在真实项目里,几乎一半的集合都是实现了IList<T>的,因为你可以方便地按编号访问、修改、插入和删除元素。比如:
- WPF或WinForms里的ViewModel接口属性,UI元素列表绑定用的。
- 实现排序、查找、换位等算法时,需要按索引访问。
- 做数据导入/导出模块,处理动态对象列表。
面试时,问IEnumerable<T>、ICollection<T>和IList<T>的区别是经典题。你只要知道每一层干嘛的,就能自信地告诉面试官为什么HashSet<T>不实现IList<T>(因为唯一性比顺序和索引更重要!)。
GO TO FULL VERSION