C# 如何使用 Index 和 Range 簡(jiǎn)化集合操作
Intro
有的語(yǔ)言數(shù)組的索引值是支持負(fù)數(shù)的,表示從后向前索引,比如:arr[-1]
從 C# 8 開(kāi)始,C# 支持了數(shù)組的反向 Index,和 Range 操作,反向 Index 類似于其他語(yǔ)言中的負(fù)索引值,但其實(shí)是由編譯器幫我們做了一個(gè)轉(zhuǎn)換,Range 使得我們對(duì)數(shù)組截取某一部分的操作會(huì)非常簡(jiǎn)單,下面來(lái)看一下如何使用吧
Sample
使用 ^ 可以從集合的最后開(kāi)始索引元素,如果從數(shù)組的最后開(kāi)始索引元素,最后一個(gè)元素應(yīng)該是 1 而不是0如: arr[^1]
使用 .. 可以基于某個(gè)數(shù)組截取集合中的某一段創(chuàng)建一個(gè)新的數(shù)組,比如 var newArray = array[1..^1],再來(lái)看一下下面的示例吧
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();
輸出結(jié)果如下:
Index
那么它是如何實(shí)現(xiàn)的呢,索引值引入了一個(gè)新的數(shù)據(jù)結(jié)構(gòu) System.Index,當(dāng)你使用 ^ 運(yùn)算符的時(shí)候,實(shí)際轉(zhuǎn)換成了 Index。
Index:
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 這種從數(shù)組最后索引的特性,只需要加一個(gè)類型是 Index 的索引器就可以了,正向索引也是支持的,int 會(huì)自動(dòng)隱式轉(zhuǎn)換為 Index,除了顯示的增加 Index 索引器之外,還可以隱式支持,實(shí)現(xiàn)一個(gè) int Count {get;} 的屬性(屬性名叫 Length 也可以),在實(shí)現(xiàn)一個(gè) int 類型的索引器就可以了
寫(xiě)一個(gè)簡(jiǎn)單的小示例:
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
Range 是在 Index 的基礎(chǔ)上實(shí)現(xiàn)的,Range 需要兩個(gè) Index 來(lái)指定開(kāi)始和結(jié)束
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 呢?
一種方式是自己直接實(shí)現(xiàn)一個(gè)類型是 Range 的索引器
另外一種方式是隱式實(shí)現(xiàn),在自定義類中添加一個(gè) Count 屬性,然后實(shí)現(xiàn)一個(gè) Slice 方法,Slice 方法有兩個(gè) int 類型的參數(shù),第一個(gè)參數(shù)表示 offset,第二個(gè)參數(shù)表示 length
來(lái)看下面這個(gè)示例吧,還是剛才那個(gè)類,我們支持一下 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; } }
More
新的操作符 (^ and ..) 都只是語(yǔ)法糖,本質(zhì)上是調(diào)用 Index、Range
Index 并不是支持負(fù)數(shù)索引,從最后向前索引只是編譯器幫我們做了一個(gè)轉(zhuǎn)換,轉(zhuǎn)換成從前到后的索引值,借助于它,我們很多取集合最后一個(gè)元素的寫(xiě)法就可以大大的簡(jiǎn)化了,就可以從原來(lái)的 array[array.Length-1] => array[^1]
Range 在創(chuàng)建某個(gè)數(shù)組的子序列的時(shí)候就非常的方便, newArray = array[1..3],需要注意的是,Range 是"左閉右開(kāi)"的,包含左邊界的值,不包含右邊界的值
還沒(méi)使用過(guò) Index/Range 的快去體驗(yàn)一下吧,用它們優(yōu)化數(shù)組的操作吧~~
以上就是C# 如何使用 Index 和 Range 簡(jiǎn)化集合操作的詳細(xì)內(nèi)容,更多關(guān)于c# 使用 Index 和 Range 簡(jiǎn)化集合操作的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#使用iTextSharp從PDF文檔獲取內(nèi)容的方法
這篇文章主要介紹了C#使用iTextSharp從PDF文檔獲取內(nèi)容的方法,涉及C#基于iTextSharp操作pdf文件的相關(guān)技巧,需要的朋友可以參考下2015-06-06基于C#實(shí)現(xiàn)12306的動(dòng)態(tài)驗(yàn)證碼變成靜態(tài)驗(yàn)證碼的方法
這篇文章主要介紹了基于C#實(shí)現(xiàn)12306的動(dòng)態(tài)驗(yàn)證碼變成靜態(tài)驗(yàn)證碼的方法的相關(guān)資料,需要的朋友可以參考下2015-12-12Revit API取得變量的內(nèi)參名稱實(shí)例代碼
這篇文章介紹了Revit API取得變量的內(nèi)參名稱實(shí)例代碼,有需要的朋友可以參考一下2013-11-11C#中實(shí)現(xiàn)PriorityQueue優(yōu)先級(jí)隊(duì)列的代碼
這篇文章主要介紹了C#中PriorityQueue優(yōu)先級(jí)隊(duì)列的實(shí)現(xiàn),構(gòu)造初始化這部分主要介紹關(guān)鍵的字段和方法,比較器的初始化以及堆的初始化,需要的朋友可以參考下2021-12-12C# OpenVINO讀取百度模型實(shí)現(xiàn)印章檢測(cè)
這篇文章主要為大家詳細(xì)介紹了C# OpenVINO如何通過(guò)直接讀取百度模型實(shí)現(xiàn)印章檢測(cè),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12C#實(shí)現(xiàn)從網(wǎng)絡(luò)同步標(biāo)準(zhǔn)北京時(shí)間的方法
這篇文章主要介紹了C#實(shí)現(xiàn)從網(wǎng)絡(luò)同步標(biāo)準(zhǔn)北京時(shí)間的方法,涉及C#操作時(shí)間的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03