18.1 一般規定
此子句引進了 擴充可索引 和 可切片集合 類型的模型,其建置在:
- 本條款中引入的類型,
System.Index(§18.2)和System.Range(§18.3); - 預先定義的一元
^(§12.9.6)和二進位..(§12.10)運算子;並且 - element_access表達式。
在模型下,類型分類為:
- 集合 (如果它 代表一組 元素s
- 如果它支援具有類型的單一引數表達式的element_access表達式,則為擴展
Index集合,該表達式透過值或引用傳回和/或設定類型的單一元素;並且 - 如果它支援具有型別的單一引數運算式的element_access表達式,則可
Range集合,該運算式會依值傳回類型元素的切片。
註: 模型不需要可以設定類型的切片,但類型可以支援它作為模型的延伸。 結尾註釋
單一維度陣列 (§12.8.12.2) 和字串 (§12.8.12.3) 支援模型。
模型可以由任何類別、結構或介面類型支援,這些類別、結構或介面類型會提供適當的索引子 (§15.9) 來實作模型語意。
模型的隱含支援是針對不直接支援模型但提供特定成員 模式 的類型 (§18.4) 提供。 此支援是型樣型,而不是語意型,因為假設其所依據的類型成員的語 意 – 語言 不會強制執行或檢查這些類型成員的語意。
就本條款而言,定義了以下術語:
- 集合是代表一組元素s 的類型。
-
可數集合是提供可數屬性的集合,即
int值實例屬性,其值是目前群組中的元素數。 該財產應命名Length為 或Count。 如果兩者都存在,則選擇前者。 -
序列或可索引類型是集合:
- 這是可數的;
- 其中每個元素都可以使用具有單一必要參數的
int表達式存取,即 from-start 索引,允許使用其他可選參數; - 如果每個元素也可以使用element_access表達式來設置,則序列是可修改的;
- 元素的 from-start 索引是序列中它之前的元素數,對於包含 N 個元素的序列:
- 第一個和最後一個元素的索引分別為 0 和 N-1,並且
- 過去結束索引是代表最後一個元素之後的假設元素的索引,其值為 N。
- 從結尾索引表示元素在序列中相對於過去結尾索引的位置。 對於包含 N 個元素的序列,第一個、最後一個和過去結束索引分別為 N、1 和 0。
- 範圍是從序列內任何索引開始的零個或多個索引的連續執行。
- 切片是範圍內元素的集合。
-
可切片集合是:
- 是可數的;
- 提供一個方法,該方法
Slice採用兩個int參數來指定一個範圍,分別是起始索引和元素計數,並返回從範圍內的元素構建的新切片。
上述定義擴展為 和 Index 的Range用途如下:
- 如果支援採用單一必要引數 (而非引數) 的
Index運算式,則類型也是int。 如果需要區分,則類型稱為 延伸可索引。 - 如果支援採用單一必要引數 (而不是方法) 的
Range運算式,則類型也是Slice。 如果需要區分,則類型稱為可擴充切片。
類型是否分類為可數、可索引或可切片,受限於成員協助工具 (§7.5) 的限制,因此取決於類型的使用位置。
範例:可數屬性和/或索引子所在
protected的類型只是其本身成員和任何衍生類型的序列。 結束範例
類型符合序列或可切片資格的必要成員可能會繼承。
範例:在下列程式代碼中
public class A { public int Length { get { … } } } public class B : A { public int this(int index) { … } } public class C : B { public int[] Slice(int index, int count) { … } }類型
A是可數的,B是序列C,並且是可切片的,也是序列。結束範例
Note:
- 類型可以是可切片的,但由於缺少 (可存取的) 索引子而無法索引。
- 若要讓類型可切片和/或可索引,需要類型是可數的。
- 雖然序列的元素是按序列中 的位置 排序的,但元素本身不需要按其值排序,甚至不需要按其值排序。
結尾註釋
18.2 索引類型
該 System.Index 型別代表一個 抽象 索引,代表從起點索引或從終點索引。
public readonly struct Index : IEquatable<Index>
{
public int Value { get; }
public bool IsFromEnd { get; }
public Index(int value, bool fromEnd = false);
public static implicit operator Index(int value);
public int GetOffset(int length);
public bool Equals(Index other);
}
Index 值是由 、 int、 指定非負偏移 bool量和 、 建構、 、 指出偏移量是從結束 (true) 還是起點 (false) 。 如果指定的位移為負數,則會擲回 。ArgumentOutOfRangeException
範例
Index first = new Index(0, false); // first element index var last = new Index(1, true); // last element index var past = new Index(0, true); // past-end index Index invalid = new Index(-1); // throws ArgumentOutOfRangeException結束範例
有一個隱含的轉換 from intIndex ,會產生從開始索引,以及語言定義的一元運算子 ^ (§12.9.6) intIndex ,從 to 會產生從結束索引。
範例
使用隱式轉換和一元
^運算子,可以寫出上述範例:Index first = 0; // first element index var last = ^1; // last element index var past = ^0; // past-end index結束範例
此方法 GetOffset 會從抽象 Index 值轉換成 int 指定 length. 如果值 Index, 是 from-end,則I此方法會傳回與 length - I.Value相同的值,否則會傳回與 I.Value相同的值。
這個方法不會檢查傳回值是否在 到 包含的0length-1有效範圍內。
便條: 未指定任何檢查,因為結果的預期用途是將索引成具有元素的
length序列,而且索引作業預期會執行適當的檢查。 結尾註釋
Indeximplements IEquatable<Index> 和值可以根據抽象值來比較相等;當且僅當各自Index的 和 Value 屬性相等時,兩個IsFromEnd值才相等。 不過 Index ,值不會排序,也不會提供其他比較作業。
便條:
Index值是無序的,因為它們是抽象索引,因此通常無法確定 from-end 索引是在不參考序列長度的情況下出現在 from start 索引之前還是之後。 一旦轉換為具體指數,例如GetOffset,這些具體指數是可比的。 結尾註釋
Index值可以直接用於element_access運算式 (§12.8.12) 的argument_list,該運算式為:
- 數組訪問,目標是單維數組(§12.8.12.2);
- 字串存取 (§12.8.12.3)
- 索引子存取,而目標類型具有索引子,其對應參數為任一
Index類型 (§12.8.12.4) 或值可隱含轉換的Index類型;或 - 索引子存取和目標類型符合指定隱含
Index支援的序列模式 (§18.4.2) 。
18.3 範圍類型
類型代表 System.Range es Index 的Start抽象範圍,從索引到索引,但不包括索引End。
public readonly struct Range : IEquatable<Index>
{
public Index Start { get; }
public Index End { get; }
public Range(Index start, Index end);
public (int Offset, int Length) GetOffsetAndLength(int length);
public bool Equals(Range other);
}
Range 值是由兩個 Index 值建構。
範例
下列範例會使用隱含的轉換
intfrom toIndex(§18.2) 和^(§12.9.6) 運算子來建立Index每個Range運算子的值:var firstQuad = new Range(0, 4); // the indices from `0` to `3` // int values impicitly convert to `Index` var nextQuad = new Range(4, 8); // the indices from `4` to `7` var wholeSeq = new Range(0, ^0); // the indices from `0` to `N-1` where `N` is the // length of the sequence wholeSeq is used with var dropFirst = new Range(1, ^0); // the indices from `1` to `N-1` var dropLast = new Range(0, ^1); // the indices from `0` to `N-2` var maybeLast = new Range(^1, 6); // the indices from `N-1` to 5 var lastTwo = new Range(^2, ^0); // the indices from `N-2` to `N-1`結束範例
語言定義的運算子 .. (§12.10) 會從值建立RangeIndex值。
範例
使用運算子可以
..寫出上述範例:var firstQuad = 0..4; // the indices from `0` to `3` var nextQuad = 4..8; // the indices from `4` to `7` var wholeSeq = 0..^0; // the indices from `0` to `N-1` var dropFirst = 1..^0; // the indices from `1` to `N-1` var dropLast = 0..^1; // the indices from `0` to `N-2` var maybeLast = ^1..6; // the indices from `N-1` to 5 var lastTwo = ^2..^0; // the indices from `N-2` to `N-1`結束範例
的 .. 運算元是選用的,第一個預設為 0,第二個預設為 ^0。
範例
上述五個範例可以藉由依賴運算元的預設值來縮短:
var firstQuad = ..4; // the indices from `0` to `3` var wholeSeq = ..; // the indices from `0` to `N-1` var dropFirst = 1..; // the indices from `1` to `N-1` var dropLast = ..^1; // the indices from `0` to `N-2` var lastTwo = ^2..; // the indices from `N-2` to `N-1`結束範例
如果出現以下情況,則值Range對於長度 L 有效:
- 相對於屬性
Range的Start的具體指數,End且在0至L的範圍內;及 - 的
Start具體索引不大於 的具體索引End
具有參數GetOffsetAndLength的方法length將抽象Range值轉換為元組表示的具體Range值。 如果 對於 Range 方法無效 length ,則擲回 ArgumentOutOfRangeException。
傳回的具體 Range 元組是一對形式 (S, N) ,其中:
-
S是範圍的起始偏移量,是 的Start屬性的Range具體索引;並且 -
N是範圍內的項目數,是 和EndStart屬性的具體索引之間的差值; - 這兩個值都是根據
length計算的。
如果為零,則具體範圍值為N。 空的具體範圍可能具有 S 等於具體過去結束索引 (§18.1) 的值,非空範圍可能沒有。 當 a Range 用來切片 (§18.1) 集合有效且相對於該集合是空的時,產生的切量是空的集合。
便條: 上述結果是,
Range相對於零的有效length且為空的值可用於對空集合進行切片,並導致空切片。 這與索引不同,索引會在集合為空時擲回例外狀況。 尾註*
範例
使用上述
GetOffSetAndLength(6)定義的變數:var (ix0, len0) = firstQuad.GetOffsetAndLength(6); // ix0 = 0, len0 = 4 var (ix1, len1) = nextQuad.GetOffsetAndLength(6); // throws // ArgumentOutOfRangeException as range crosses sequence end var (ix2, len2) = wholeSeq.GetOffsetAndLength(6); // ix2 = 0, len2 = 6 var (ix3, len3) = dropFirst.GetOffsetAndLength(6); // ix3 = 1, len3 = 5 var (ix4, len4) = dropLast.GetOffsetAndLength(6); // ix4 = 0, len4 = 5 var (ix5, len5) = maybeLast.GetOffsetAndLength(6); // ix5 = 5, len5 = 1 var (ix6, len6) = lastTwo.GetOffsetAndLength(6); // ix6 = 4, len6 = 2
Range可以根據抽象值比較工具IEquatable<Range>和值是否相等;當且僅當相應 Range 和 Start 屬性的抽象值相等時,兩個End值相等 (§18.2)。 不過 Range ,值不會排序,也不會提供其他比較作業。
便條:
Range值是無序的,因為它們是抽象的,並且沒有唯一的排序關係。 一旦轉換為具體的起點和長度,例如,GetOffsetAndLength就可以定義排序關係。 結尾註釋
Range值可以直接用於element_access運算式 (§12.8.12) 的argument_list,即:
- 數組訪問,目標是單維數組(§12.8.12.2);
- 字串存取 (§12.8.12.3);
- 索引子存取,而目標類型具有索引子,其對應參數為任一
Range類型 (§12.8.12.4) 或值可隱含轉換的Range類型;或 - 索引子存取 (§12.8.12.4) 和目標類型符合指定隱含
Range支援的序列模式 (§18.4.3) 。
18.4 基於模式的隱含支援索引和範圍
18.4.1 一般規定
如果形式的element_access表達式 (§12.8.12) E[A]E ;where 具有類型T,並且A是隱含可Index轉換為 或 Range的單一表達式,則無法識別為:
- 陣列存取 (§12.8.12.2),
- 字串存取 (§12.8.12.3),或
- 索引子存取 (§12.8.12.4)
TAS 不提供適當的可存取索引子
然後,如果符合特定模式,則 T 會提供運算式的隱含支援。 如果不符合此型樣,則 T 會發生編譯階段錯誤。
18.4.2 隱含索引支援
如果在任何element_access上下文中,格式為 ;where 具有類型E[A]且E是隱含可轉換為 T; 的單一表達式 (AIndex18.4.1) 不有效 (§18.4.1),則如果在相同的上下文中:
-
T提供可存取的成員,使其限定為 序列 (§18.1);和 - 運算式
E[0]有效,並使用符合序列資格T的相同索引子
則該表達式 E[A] 應被隱含支持。
在不對本標準的實施進行其他限制的情況下,表達式的評估順序應等同於:
-
E被評估; -
A被評估; - 如果實現需要,則評估 的
T可數屬性; - 會叫用在相同內容中使用的基礎
int索引子T的E[0]GET 或 SET 存取子。
18.4.3 隱式範圍支援
如果在任何element_access上下文中,格式為 ;where 具有類型E[A]且E是隱含可轉換為 T; 的單一表達式 (ARange18.4.1) 不有效 (§18.4.1),則如果在相同的上下文中:
-
T提供可存取的成員,使其同時符合 可數 和 可切片 的資格 (§18.1)
則該表達式 E[A] 應被隱含支持。
在不對本標準的實施進行其他限制的情況下,表達式的評估順序應等同於:
-
E被評估; -
A被評估; - 如果實現需要,則評估 的
T可數屬性; -
Slice的方法會呼叫 。T