使用索引子 (C# 程式設計手冊)
索引子帶來語法的便利性,讓您建立的 class、struct 或 interface 可供用戶端應用程式當作陣列一樣存取。 編譯器會產生 Item
屬性 (如果 IndexerNameAttribute 存在,則產生另外命名的屬性) 和相應的存取子方法。 索引子最常實作於類型中,而類型的主要用途是封裝內部集合或陣列。 例如,假設您有類別 TempRecord
會指出 24 小時期間內,10 個不同時間的華氏溫度記錄。 此類別包含類型為 float[]
的陣列 temps
,可以儲存溫度值。 透過在此類別中實作索引子,用戶端能以 TempRecord
執行個體中 float temp = tempRecord[4]
的形式 (而非 float temp = tempRecord.temps[4]
) 來存取溫度。 索引子標記法不只簡化了用戶端應用程式的語法,還能讓其他開發人員更容易了解此類別及其用途。
若要在類別或結構上宣告索引子,請使用 this 關鍵字,如下列範例所示:
// Indexer declaration
public int this[int index]
{
// get and set accessors
}
重要
宣告索引子會自動為物件產生名為 Item
的屬性。 Item
屬性無法直接從執行個體成員存取運算式直接存取。 此外,若您將自己的 Item
屬性,新增至包含索引子的物件,將會收到 CS0102 編譯器錯誤。 若要避免此錯誤,請使用本文稍後詳述的 IndexerNameAttribute 重新命名索引子。
備註
索引子類型和其參數類型至少必須可以像索引子本身一樣地存取。 如需存取範圍層級的詳細資訊,請參閱存取修飾詞。
如需如何搭配使用索引子與介面的詳細資訊,請參閱介面索引子。
索引子的簽章包含其型式參數的數目和類型。 它不包含索引子類型或正式參數的名稱。 如果您在相同的類別中宣告多個索引子,則它們必須具有不同的簽章。
索引子並非歸類為變數,因此,索引子的值不能經由參照 (以 ref
或 out
參數的形式) 來傳遞,除非它的值是參照 (亦即會經由參照來傳回)。
若要以其他語言可使用的名稱提供索引子,請使用 System.Runtime.CompilerServices.IndexerNameAttribute,如下列範例所示:
// Indexer declaration
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
// get and set accessors
}
此索引子具有名稱 TheItem
,因為它已由索引子名稱屬性覆寫。 根據預設,此索引子的名稱為 Item
。
範例 1
下列範例示範如何宣告私用陣列欄位 temps
和索引子。 索引子可讓您直接存取執行個體 tempRecord[i]
。 使用索引子的替代方式是將陣列宣告為 public 成員,並直接存取其成員 tempRecord.temps[i]
。
public class TempRecord
{
// Array of temperature values
float[] temps =
[
56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
61.3F, 65.9F, 62.1F, 59.2F, 57.5F
];
// To enable client code to validate input
// when accessing your indexer.
public int Length => temps.Length;
// Indexer declaration.
// If index is out of range, the temps array will throw the exception.
public float this[int index]
{
get => temps[index];
set => temps[index] = value;
}
}
請注意,評估索引子的存取時 (例如,在 Console.Write
陳述式中),會叫用 get 存取子。 因此,如果沒有 get
存取子,就會發生編譯時間錯誤。
var tempRecord = new TempRecord();
// Use the indexer's set accessor
tempRecord[3] = 58.3F;
tempRecord[5] = 60.1F;
// Use the indexer's get accessor
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Element #{i} = {tempRecord[i]}");
}
使用其他值編製索引
C# 不會將索引子參數類型限制為整數。 例如,搭配索引子使用字串可能十分有用。 在集合中搜尋字串,並傳回適當的值,可能會實作這類索引子。 存取子有可能多載,因此可能會有多種版本的字串和整數並存。
範例 2
下列範例宣告的類別會儲存週中的日。 get
存取子接受字串 (日的名稱),並傳回對應的整數。 例如,"Sunday" 會傳回 0,"Monday" 會傳回 1,依此類推。
// Using a string as an indexer value
class DayCollection
{
string[] days = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"];
// Indexer with only a get accessor with the expression-bodied definition:
public int this[string day] => FindDayIndex(day);
private int FindDayIndex(string day)
{
for (int j = 0; j < days.Length; j++)
{
if (days[j] == day)
{
return j;
}
}
throw new ArgumentOutOfRangeException(
nameof(day),
$"Day {day} is not supported.\nDay input must be in the form \"Sun\", \"Mon\", etc");
}
}
取用範例 2
var week = new DayCollection();
Console.WriteLine(week["Fri"]);
try
{
Console.WriteLine(week["Made-up day"]);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine($"Not supported input: {e.Message}");
}
範例 3
下列範例宣告的類別,會儲存使用 System.DayOfWeek 列舉的週間日。 get
存取子接受 DayOfWeek
(週間日的值),並會傳回對應的整數。 例如,DayOfWeek.Sunday
會傳回 0、DayOfWeek.Monday
日傳回 1 等等。
using Day = System.DayOfWeek;
class DayOfWeekCollection
{
Day[] days =
[
Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday,
Day.Thursday, Day.Friday, Day.Saturday
];
// Indexer with only a get accessor with the expression-bodied definition:
public int this[Day day] => FindDayIndex(day);
private int FindDayIndex(Day day)
{
for (int j = 0; j < days.Length; j++)
{
if (days[j] == day)
{
return j;
}
}
throw new ArgumentOutOfRangeException(
nameof(day),
$"Day {day} is not supported.\nDay input must be a defined System.DayOfWeek value.");
}
}
取用範例 3
var week = new DayOfWeekCollection();
Console.WriteLine(week[DayOfWeek.Friday]);
try
{
Console.WriteLine(week[(DayOfWeek)43]);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine($"Not supported input: {e.Message}");
}
穩固程式設計
有兩種主要的方式可以改善索引子的安全性和可靠性:
請務必包含某種類型的錯誤處理策略來處理用戶端程式碼傳入無效索引值的機會。 在本文章稍早的第一個範例中,TempRecord 類別提供 Length 屬性,讓用戶端程式碼先確認輸入,再傳遞給索引子。 您也可以將錯誤處理程式碼放在索引子本身內。 請務必為使用者記載您在索引子存取子內擲回的任何例外狀況。
將 get 與 set 存取子的存取範圍設定為合理限制。 這對
set
存取子特別重要。 如需詳細資訊,請參閱限制存取子的存取範圍。