有的語言數組的索引值是支持負數的,表示從後向前索引,好比:arr[-1]
git
從 C# 8 開始,C# 支持了數組的反向 Index
,和 Range
操做,反向 Index
相似於其餘語言中的負索引值,但實際上是由編譯器幫咱們作了一個轉換,Range
使得咱們對數組截取某一部分的操做會很是簡單,下面來看一下如何使用吧github
使用 ^
能夠從集合的最後開始索引元素,若是從數組的最後開始索引元素,最後一個元素應該是 1 而不是0如: arr[^1]
c#
使用 ..
能夠基於某個數組截取集合中的某一段建立一個新的數組,好比 var newArray = array[1..^1]
,再來看一下下面的示例吧api
int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; int lastElement = someArray[^1]; // lastElement = 5 lastElement.Dump(); someArray[3..5].Dump(); someArray[1..^1].Dump(); someArray[1..].Dump(); someArray[..^1].Dump(); someArray[..2].Dump();
輸出結果以下:數組
那麼它是如何實現的呢,索引值引入了一個新的數據結構 System.Index
,當你使用 ^
運算符的時候,實際轉換成了 Index
。數據結構
Index
:ide
public readonly struct Index : IEquatable<Index> { public Index(int value, bool fromEnd = false); /// <summary>Create an Index pointing at first element.</summary> public static Index Start => new Index(0); /// <summary>Create an Index pointing at beyond last element.</summary> public static Index End => new Index(~0); // // Summary: // Gets a value that indicates whether the index is from the start or the end. // // Returns: // true if the Index is from the end; otherwise, false. public bool IsFromEnd { get; } // // Summary: // Gets the index value. // // Returns: // The index value. public int Value { get; } // // Summary: // Creates an System.Index from the end of a collection at a specified index position. // // Parameters: // value: // The index value from the end of a collection. // // Returns: // The Index value. public static Index FromEnd(int value); // // Summary: // Create an System.Index from the specified index at the start of a collection. // // Parameters: // value: // The index position from the start of a collection. // // Returns: // The Index value. public static Index FromStart(int value); // // Summary: // Returns a value that indicates whether the current object is equal to another // System.Index object. // // Parameters: // other: // The object to compare with this instance. // // Returns: // true if the current Index object is equal to other; false otherwise. public bool Equals(Index other); // // Summary: // Calculates the offset from the start of the collection using the given collection length. // // Parameters: // length: // The length of the collection that the Index will be used with. Must be a positive value. // // Returns: // The offset. public int GetOffset(int length); // // Summary: // Converts integer number to an Index. // // Parameters: // value: // The integer to convert. // // Returns: // An Index representing the integer. public static implicit operator Index(int value); }
若是想要本身自定義的集合支持 Index
這種從數組最後索引的特性,只須要加一個類型是 Index
的索引器就能夠了,正向索引也是支持的,int 會自動隱式轉換爲 Index
,除了顯示的增長 Index
索引器以外,還能夠隱式支持,實現一個 int Count {get;}
的屬性(屬性名叫 Length
也能夠),在實現一個 int
類型的索引器就能夠了優化
寫一個簡單的小示例:this
private class TestCollection { public IList<int> Data { get; init; } public int Count => Data.Count; public int this[int index] => Data[index]; //public int this[Index index] => Data[index.GetOffset(Data.Count)]; } var array = new TestCollection() { Data = new[] { 1, 2, 3 } }; Console.WriteLine(array[^1]); Console.WriteLine(array[1]);
Range
是在 Index
的基礎上實現的,Range
須要兩個 Index
來指定開始和結束code
public readonly struct Range : IEquatable<Range> { /// <summary>Represent the inclusive start index of the Range.</summary> public Index Start { get; } /// <summary>Represent the exclusive end index of the Range.</summary> public Index End { get; } /// <summary>Construct a Range object using the start and end indexes.</summary> /// <param name="start">Represent the inclusive start index of the range.</param> /// <param name="end">Represent the exclusive end index of the range.</param> public Range(Index start, Index end) { Start = start; End = end; } /// <summary>Create a Range object starting from start index to the end of the collection.</summary> public static Range StartAt(Index start) => new Range(start, Index.End); /// <summary>Create a Range object starting from first element in the collection to the end Index.</summary> public static Range EndAt(Index end) => new Range(Index.Start, end); /// <summary>Create a Range object starting from first element to the end.</summary> public static Range All => new Range(Index.Start, Index.End); /// <summary>Calculate the start offset and length of range object using a collection length.</summary> /// <param name="length">The length of the collection that the range will be used with. length has to be a positive value.</param> /// <remarks> /// For performance reason, we don't validate the input length parameter against negative values. /// It is expected Range will be used with collections which always have non negative length/count. /// We validate the range is inside the length scope though. /// </remarks> public (int Offset, int Length) GetOffsetAndLength(int length); }
如何在本身的類中支持 Range
呢?
一種方式是本身直接實現一個類型是 Range
的索引器
另一種方式是隱式實現,在自定義類中添加一個 Count
屬性,而後實現一個 Slice
方法,Slice
方法有兩個 int
類型的參數,第一個參數表示 offset,第二個參數表示 length
來看下面這個示例吧,仍是剛纔那個類,咱們支持一下 Range
:
private class TestCollection { public IList<int> Data { get; init; } //public int[] this[Range range] //{ // get // { // var rangeInfo = range.GetOffsetAndLength(Data.Count); // return Data.Skip(rangeInfo.Offset).Take(rangeInfo.Length).ToArray(); // } //} public int Count => Data.Count; public int[] Slice(int start, int length) { var array = new int[length]; for (var i = start; i < length && i < Data.Count; i++) { array[i] = Data[i]; } return array; } }
新的操做符 (^
and ..
) 都只是語法糖,本質上是調用 Index
、Range
Index
並非支持負數索引,從最後向前索引只是編譯器幫咱們作了一個轉換,轉換成從前到後的索引值,藉助於它,咱們不少取集合最後一個元素的寫法就能夠大大的簡化了,就能夠從原來的 array[array.Length-1]
=> array[^1]
Range
在建立某個數組的子序列的時候就很是的方便, newArray = array[1..3]
,須要注意的是,Range
是"左閉右開"的,包含左邊界的值,不包含右邊界的值
還沒使用過 Index
/Range
的快去體驗一下吧,用它們優化數組的操做吧~~