共用方式為


18 擴展索引和切片

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 參數來指定一個範圍,分別是起始索引和元素計數,並返回從範圍內的元素構建的新切片。

上述定義擴展為 和 IndexRange用途如下:

  • 如果支援採用單一必要引數 (而非引數) 的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.6intIndex ,從 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 IndexStart抽象範圍,從索引到索引,但不包括索引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 值建構。

範例

下列範例會使用隱含的轉換 int from to Index§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 有效:

  • 相對於屬性RangeStart的具體指數,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>和值是否相等;當且僅當相應 RangeStart 屬性的抽象值相等時,兩個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.12E[A]E ;where 具有類型T,並且A是隱含可Index轉換為 或 Range的單一表達式,則無法識別為:

然後,如果符合特定模式,則 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] 應被隱含支持。

在不對本標準的實施進行其他限制的情況下,表達式的評估順序應等同於:

  1. E 被評估;
  2. A 被評估;
  3. 如果實現需要,則評估 的 T 可數屬性;
  4. 會叫用在相同內容中使用的基礎int索引子TE[0] GET 或 SET 存取子。

18.4.3 隱式範圍支援

如果在任何element_access上下文中,格式為 ;where 具有類型E[A]E是隱含可轉換為 T; 的單一表達式 (ARange18.4.1) 不有效 (§18.4.1),則如果在相同的上下文中:

  • T 提供可存取的成員,使其同時符合 可數可切片 的資格 (§18.1

則該表達式 E[A] 應被隱含支持。

在不對本標準的實施進行其他限制的情況下,表達式的評估順序應等同於:

  1. E 被評估;
  2. A 被評估;
  3. 如果實現需要,則評估 的 T 可數屬性;
  4. Slice的方法會呼叫 。T