C# 中 System.Range 結構體

技術譯民發表於2020-10-13

翻譯自 John Demetriou 2020年4月6日 的文章 《C# 8 Is Introducing Ranges》

我們之前討論過的 C# 中的一個特性 System.Index 結構體[1],與另一特性 System.Range 結構體 是攜手並進的。

在 C# 8.0 之前,沒有訪問集合中任意部分的範圍或切片的語法。開發人員通常不得不執行一些自定義的切片操作,或者依靠諸如 array.Skip(5).Take(2); 的 LINQ 方法。

現在 C# 新增了一個新的小特性,它可以幫助人們從一個大的陣列專案中獲取一個小範圍的專案。

讓我們來考慮一下。假設我們有一個專案陣列,我們僅需展示其中的一部分。通常情況下,我們只需要迴圈這部分,然後像下面這樣輸出它們。

var array = new string[]
    {
        "Item0",
        "Item1",
        "Item2",
        "Item3",
        "Item4",
        "Item5",
        "Item6",
        "Item7",
        "Item8",
        "Item9"
    };

for (int i = 1; i <= 5; i++)
{
    Console.WriteLine(array[i]);
}

因此當我們僅需要幾個特定的項,我們通過控制索引值來獲取它們。

通過使用範圍(Range),我們可以用以下方式來替換:

var array = new string[]
    {
        "Item0",
        "Item1",
        "Item2",
        "Item3",
        "Item4",
        "Item5",
        "Item6",
        "Item7",
        "Item8",
        "Item9"
    };

foreach (var item in array[1..5])
{
    Console.WriteLine(item);
}

在這裡,第一次使用的人會注意到一個奇怪的情況——它只會列印四項。為什麼呢?這是因為範圍(Range)包含開始,但不包含末尾
如果想要與上面的 for 迴圈有相同的結果,我們的 foreach 迴圈應該是:

foreach (var item in array[1..6])
{
    Console.WriteLine(item);
}

Range 的一些示例

1、從索引 1(包含) 到末尾(包含)

foreach (var item in array[1..])
{
    Console.WriteLine(item);
}

2、從開始(包含)到索引 3(不包含)

foreach (var item in array[..3])
{
    Console.WriteLine(item);
}

3、結合以上兩種,您可以得到一個語法上正確的以下版本

foreach (var item in array[..])
{
    Console.WriteLine(item);
}

它的意思是獲取整個範圍。

譯者注
它也等同於下面兩種寫法:

foreach (var item in array[0..^0])
{
    Console.WriteLine(item);
}
foreach (var item in array[0..array.Length])
{
    Console.WriteLine(item);
}

4、從索引 1(包含)到 從末尾開始的索引 1(不包含)

foreach (var item in array[1..^1])
{
    Console.WriteLine(item);
}

在這裡,它結合了[前面文章][1:1]中介紹的 Index 的 Hat 運算子(^)。

一個快速的解釋,Hat 運算子(^),會給您一個特定的索引。如果您寫 ^1,也就是在請求最後一項的索引。如上所述,考慮到最後一個索引號是排除的,在一個有 10 項的序列中,請求索引項直到 ^1,您也就是在請求索引項直到索引 9(索引從 0 開始),並且該項不包括在其中。
我希望我講清楚了。

將範圍作為變數

我們前面所做的是,將範圍(Range)作為型別傳遞。我們也可以將範圍宣告為一個變數:

Range range = 1..9;

然後,可以在 [] 字元中使用該範圍:

var subarray = array[range];

我們還可以將其作為方法的引數傳遞、將其儲存為私有變數,或者用在任何業務邏輯所需的地方。

範圍的型別支援

範圍(Range)不能被用在 List 或者 IEnumerable<T>,但陣列並不是唯一支援它的型別。

您還可以將索引(Index)和範圍(Range) 與 StringSpan<T>[2]ReadOnlySpan<T>[3] 一起使用。

與字串一起使用的例子基本上就是代替 Substring 方法,如下所示:

string s = "01234567";
string r = s[1..3]; // r 將會是 "12"

對於每個擔心 List(它可能是您通常用來替代陣列的型別)不支援範圍的人來說,List 從 .Net Framework 2.0 開始就已經有 GetRange 方法了。




以下為譯者補充

索引和範圍總結

索引(Index)和範圍(Range)為訪問序列中的單個元素或範圍提供了簡潔的語法。

對索引和範圍的語言支援依賴於兩個新型別兩個新運算子

  • System.Index[4] 型別表示一個序列索引。
  • Hat 運算子(^),指定一個索引與序列末尾相關,用於構造 System.Index 物件。
  • System.Range[5] 型別表示序列的子範圍。
  • 範圍運算子(..),用於指定範圍的開始和末尾,就像運算元一樣,用於構造 System.Range 物件。

考慮陣列 sequence0 索引與 sequence[0] 相同。 ^0 索引與 sequence[sequence.Length] 相同。 請注意,sequence[..^0] 不會引發異常,就像 sequence[..sequence.Length] 一樣。對於任意數字 n,索引 ^nsequence.Length - n 相同。 如下面程式碼中的註釋所示:

string[] words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

一個範圍指定了範圍的“開始”“末尾”。 範圍是左閉右開的,也就是說範圍包含“開始”,不包含“末尾”
範圍 [0..sequence.Length][0..^0][..] 都表示整個範圍。


作者 : John Demetriou
譯者 : 技術譯民
出品 : 技術譯站
連結 : 英文原文


  1. https://www.cnblogs.com/ittranslator/p/13810800.html System.Index 結構體和 Hat 運算子(^)的全新用法 ↩︎ ↩︎

  2. https://docs.microsoft.com/zh-cn/dotnet/api/system.span-1 Span<T> ↩︎

  3. https://docs.microsoft.com/zh-cn/dotnet/api/system.readonlyspan-1 ReadOnlySpan<T> ↩︎

  4. https://docs.microsoft.com/zh-cn/dotnet/api/system.index System.Index ↩︎

  5. https://docs.microsoft.com/zh-cn/dotnet/api/system.range System.Range ↩︎

相關文章