1. 程式人生 > 程式設計 >詳解c#索引(Index)和範圍(Range)

詳解c#索引(Index)和範圍(Range)

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

在本教程中,你將瞭解:

  • 對某個序列中的範圍使用該語法。
  • 瞭解每個序列開頭和末尾的設計決策。
  • 瞭解 Index 和 Range 型別的應用場景。

對索引和範圍的語言支援

此語言支援依賴於兩個新型別和兩個新運算子:

  • System.Index 表示一個序列索引。
  • 來自末尾運算子 ^ 的索引,指定一個索引與序列末尾相關。
  • System.Range 表示序列的子範圍。
  • 範圍運算子 ..,用於指定範圍的開始和末尾,就像運算元一樣。

讓我們從索引規則開始。 請考慮陣列 sequence0 索引與 sequence[0]

相同。 ^0 索引與 sequence[sequence.Length] 相同。 表示式 sequence[^0] 不會引發異常,就像 sequence[sequence.Length] 一樣。 對於任何數字 n,索引 ^nsequence[sequence.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

可以使用 ^1 索引檢索最後一個詞。 在初始化下面新增以下程式碼:

Console.WriteLine($"The last word is {words[^1]}");

範圍指定範圍的開始和末尾。 範圍是排除的,也就是說“末尾”不包含在範圍內。 範圍 [0..^0] 表示整個範圍,就像 [0..sequence.Length] 表示整個範圍。
以下程式碼建立了一個包含單詞“quick”、“brown”和“fox”的子範圍。 它包括 words[1] 到 words[3]。 元素 words[4] 不在該範圍內。 將以下程式碼新增到同一方法中。 將其複製並貼上到互動式視窗的底部。

string[] quickBrownFox = words[1..4];
foreach (var word in quickBrownFox)
  Console.Write($"< {word} >");
Console.WriteLine();

以下程式碼使用“lazy”和“dog”返回範圍。 它包括 words[^2]words[^1]。 結束索引 words[^0] 不包括在內。 同樣新增以下程式碼:

string[] lazyDog = words[^2..^0];
foreach (var word in lazyDog)
  Console.Write($"< {word} >");
Console.WriteLine();

下面的示例為開始和/或結束建立了開放範圍:

string[] allWords = words[..]; // contains "The" through "dog".
string[] firstPhrase = words[..4]; // contains "The" through "fox"
string[] lastPhrase = words[6..]; // contains "the,"lazy" and "dog"
foreach (var word in allWords)
  Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in firstPhrase)
  Console.Write($"< {word} >");
Console.WriteLine();
foreach (var word in lastPhrase)
  Console.Write($"< {word} >");
Console.WriteLine();

還可以將範圍或索引宣告為變數。 然後可以在 [ 和 ] 字元中使用該變數:

Index the = ^3;
Console.WriteLine(words[the]);
Range phrase = 1..4;
string[] text = words[phrase];
foreach (var word in text)
  Console.Write($"< {word} >");
Console.WriteLine();

下面的示例展示了使用這些選項的多種原因。 請修改 x、y 和 z 以嘗試不同的組合。 在進行實驗時,請使用 x 小於 y且 y 小於 z 的有效組合值。 在新方法中新增以下程式碼。 嘗試不同的組合:

int[] numbers = Enumerable.Range(0,100).ToArray();
int x = 12;
int y = 25;
int z = 36;

Console.WriteLine($"{numbers[^x]} is the same as {numbers[numbers.Length - x]}");
Console.WriteLine($"{numbers[x..y].Length} is the same as {y - x}");

Console.WriteLine("numbers[x..y] and numbers[y..z] are consecutive and disjoint:");
Span<int> x_y = numbers[x..y];
Span<int> y_z = numbers[y..z];
Console.WriteLine($"\tnumbers[x..y] is {x_y[0]} through {x_y[^1]},numbers[y..z] is {y_z[0]} through {y_z[^1]}");

Console.WriteLine("numbers[x..^x] removes x elements at each end:");
Span<int> x_x = numbers[x..^x];
Console.WriteLine($"\tnumbers[x..^x] starts with {x_x[0]} and ends with {x_x[^1]}");

Console.WriteLine("numbers[..x] means numbers[0..x] and numbers[x..] means numbers[x..^0]");
Span<int> start_x = numbers[..x];
Span<int> zero_x = numbers[0..x];
Console.WriteLine($"\t{start_x[0]}..{start_x[^1]} is the same as {zero_x[0]}..{zero_x[^1]}");
Span<int> z_end = numbers[z..];
Span<int> z_zero = numbers[z..^0];
Console.WriteLine($"\t{z_end[0]}..{z_end[^1]} is the same as {z_zero[0]}..{z_zero[^1]}");

索引和範圍的型別支援

索引和範圍提供清晰、簡潔的語法來訪問序列中的單個元素或元素的範圍。 索引表示式通常返回序列元素的型別。 範圍表示式通常返回與源序列相同的序列型別。
若任何型別提供帶 Index 或 Range 引數的索引器,則該型別可分別顯式支援索引或範圍。 採用單個 Range 引數的索引器可能會返回不同的序列型別,如 System.Span<T>。

重要

使用範圍運算子的程式碼的效能取決於序列運算元的型別。
範圍運算子的時間複雜度取決於序列型別。 例如,如果序列是 string 或陣列,則結果是輸入中指定部分的副本,因此,時間複雜度為 O(N)(其中 N 是範圍的長度)。 另一方面,如果它是 System.Span<T> System.Memory<T>,則結果引用相同的後備儲存,這意味著沒有副本且操作為 O(1)。
除了時間複雜度外,這還會產生額外的分配和副本,從而影響效能。 在效能敏感的程式碼中,考慮使用 Span<T>Memory<T> 作為序列型別,因為不會為其分配範圍運算子。

若型別包含名稱為 Length Count 的屬性,屬性有可訪問的 Getter 並且其返回型別為 int,則此型別為可計數型別。**** 不顯式支援索引或範圍的可計數型別可能為它們提供隱式支援。 有關詳細資訊,請參閱功能建議說明的隱式索引支援和隱式範圍支援部分。 使用隱式範圍支援的範圍將返回與源序列相同的序列型別。
例如,以下 .NET 型別同時支援索引和範圍:String、Span<T> 和 ReadOnlySpan<T>。 List<T> 支援索引,但不支援範圍。
Array 具有更多的微妙行為。 單個維度陣列同時支援索引和範圍。 多維陣列則不支援。 多維陣列的索引器具有多個引數,而不是一個引數。 交錯陣列(也稱為陣列的陣列)同時支援範圍和索引器。 下面的示例演示如何迴圈訪問交錯陣列的矩形子節。 它迴圈訪問位於中心的節,不包括前三行和後三行,以及每個選定行中的前兩列和後兩列:

var jagged = new int[10][]
{
  new int[10] { 0,1,2,3,4,5,6,7,8,9},new int[10] { 10,11,12,13,14,15,16,17,18,19},new int[10] { 20,21,22,23,24,25,26,27,28,29},new int[10] { 30,31,32,33,34,35,36,37,38,39},new int[10] { 40,41,42,43,44,45,46,47,48,49},new int[10] { 50,51,52,53,54,55,56,57,58,59},new int[10] { 60,61,62,63,64,65,66,67,68,69},new int[10] { 70,71,72,73,74,75,76,77,78,79},new int[10] { 80,81,82,83,84,85,86,87,88,89},new int[10] { 90,91,92,93,94,95,96,97,98,99},};

var selectedRows = jagged[3..^3];

foreach (var row in selectedRows)
{
  var selectedColumns = row[2..^2];
  foreach (var cell in selectedColumns)
  {
    Console.Write($"{cell},");
  }
  Console.WriteLine();
}

在所有情況下,Array 的範圍運算子都會分配一個數組來儲存返回的元素。

索引和範圍的應用場景

要分析較大序列的一部分時,通常會使用範圍和索引。 在準確讀取所涉及的序列部分這一方面,新語法更清晰。 本地函式 MovingAverageRange 為引數。 然後,該方法在計算最小值、最大值和平均值時僅列舉該範圍。 在專案中嘗試以下程式碼:

int[] sequence = Sequence(1000);

for(int start = 0; start < sequence.Length; start += 100)
{
  Range r = start..(start+10);
  var (min,max,average) = MovingAverage(sequence,r);
  Console.WriteLine($"From {r.Start} to {r.End}:  \tMin: {min},\tMax: {max},\tAverage: {average}");
}

for (int start = 0; start < sequence.Length; start += 100)
{
  Range r = ^(start + 10)..^start;
  var (min,r);
  Console.WriteLine($"From {r.Start} to {r.End}: \tMin: {min},\tAverage: {average}");
}

(int min,int max,double average) MovingAverage(int[] subSequence,Range range) =>
  (
    subSequence[range].Min(),subSequence[range].Max(),subSequence[range].Average()
  );

int[] Sequence(int count) =>
  Enumerable.Range(0,count).Select(x => (int)(Math.Sqrt(x) * 100)).ToArray();

以上就是詳解c#索引(Index)和範圍(Range)的詳細內容,更多關於c# 索引和範圍的資料請關注我們其它相關文章!