CodeGym /課程 /C# SELF /索引存取合約: IList<T>

索引存取合約: IList<T>

C# SELF
等級 28 , 課堂 3
開放

1. 前言

想像一下:你有個集合……但你想要很快拿到第三個、第七個,或是第零個元素。或是想把它們交換位置。陣列這種東西超簡單 — 用索引就搞定了(array[3])。那集合呢?不是每種集合都能這樣隨便用索引喔!

這時候 IList<T> 介面就登場啦 — 它是一個通用合約,要求集合必須支援用索引存取元素。簡單說:只要你的集合有實作 IList<T>,你就可以像操作陣列一樣用 索引 直接存取或改值。

比喻:
想像圖書館的卡片:每本書在書架上都有自己的編號,你隨時可以走過去拿「第三本」書。支援 IList<T> 的集合就是這種感覺。

2. IList<T> 的結構與方法

IList<T> 介面是很多集合的主角。它繼承自 ICollection<T>(而 ICollection<T> 又繼承 IEnumerable<T>,所以可以用 foreach 之類的迴圈跑),然後加上最重要的:索引操作。

介面繼承結構圖:


IEnumerable<T>
      ▲
      │
ICollection<T>
      ▲
      │
  IList<T>
.NET 集合介面繼承關係

IList<T> 主要成員

成員 用途
T this[int index] { get; set; }
用索引取得或設定元素
int IndexOf(T item)
找出元素第一次出現的索引
void Insert(int index, T item)
在指定位置插入元素
void RemoveAt(int index)
用索引刪除元素

其他像 AddRemoveClearContains 這些,都是從 ICollection<T> 來的。

重點:Indexer(索引子)

IList<T> 最大的特色就是有 indexer。這是一種語法糖,讓你可以這樣寫:


var myList = new List<int> { 10, 20, 30 };
int secondValue = myList[1]; // 取得 20
myList[2] = 42;              // 改第三個元素

3. 哪些集合有實作 IList<T>

在 .NET 標準函式庫裡,很多常見的資料結構都支援 IList<T>。來看看最熱門的幾個:

集合 可索引 說明
List<T>
可以 動態陣列
T[]
可以 一般陣列都能用索引
BindingList<T>
可以 用於資料繫結(data binding)
ObservableCollection<T>
可以 有通知功能的清單
Collection<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)),但如果你自己用 linked list 實作一個集合然後硬加 IList<T>,那存取就會變慢。雖然標準函式庫不會這樣搞。

還有一點:如果你把陣列當 IList<T> 用,可以改元素,但不能改陣列長度。像 AddRemoveInsert 這些方法對陣列會丟 NotSupportedException


int[] myArray = { 1, 2, 3 };
IList<int> listView = myArray; // Upcast

listView[0] = 42; // OK!
listView.Add(99); // 會丟 NotSupportedException

6. 實務應用與為什麼要這樣設計

在實際專案裡,幾乎每兩個集合就有一個是實作 IList<T>,因為用索引存取、改值、插入、刪除都超方便。舉例來說:

  • WPF 或 WinForms 的 ViewModel 裡的介面屬性,UI 元素清單會綁定到這種集合。
  • 排序、搜尋、置換等演算法,需要用索引存取元素。
  • 資料匯入/匯出模組,會操作動態物件清單。

面試時,問 IEnumerable<T>ICollection<T>IList<T> 差在哪,超常見。你只要知道每一層負責什麼,就能很穩地跟面試官解釋為什麼 HashSet<T> 沒有實作 IList<T>(因為唯一性比順序和索引重要!)。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION