C#8.0 Range and Index

  • 57
  • 0

C#8.0有關陣列index中的"點點"運算子

先來一個單元測試範例

int[] a = new int[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

// StartAt 2,EndAt 5 (不包含5)
Assert.AreEqual(a[2..5], new int[] { 7, 8, 9 });
Assert.AreEqual(a[new Range(new Index(2), new Index(5))], new int[] { 7, 8, 9 });

a[2..5]中的2..5代表一個Range,可用來取出指定Index範圍的集合
2與5分別代表著Index,Index可由前開始或由後開始,如果由後開始可加上^運算子,array[^x] 會被轉為 array[array.Length - x]
所以fromEnd(由後開始)時最後一個元素是array[^1]

fromEnd範例如下

int[] a = new int[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

// FromEnd
Assert.AreEqual(a[2..^2], new int[] { 7, 8, 9, 10, 11, 12, 13 });

// 因為轉換機制是array.Length - x,^0寫在Range的End時是沒問題,一旦取到就是Index out of bound
// a[^0] thorws Exception
Assert.AreEqual(a[^11], 5);
Assert.AreEqual(a[^1], 15);

index範例

array:  { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }
index:     0,   1,   2,   3,   4,   5,   6,   7
fromEnd:  ^8,  ^7,  ^6,  ^5,  ^4,  ^3,  ^2,  ^1

而Range寫為expr1..expr2,其中expr1、expr2可省略
如果省略了expr1,就相當於0..expr2,類似SkipLast概念
如果省略了expr2,就相當於expr1..^0,類似Skip概念
如果都省略了(只剩下..),就相當於0..^0 (Range.All)

配上Linq的Append、Prepend,循環左移、循環右移也可以更簡短了

int[] a = new int[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

// RightShift
Assert.AreEqual(a[..^1].Prepend(a[^1]), new int[] { 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 });


// LeftShift
Assert.AreEqual(a[1..].Append(a[0]), new int[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5 });

參考:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges