目录
????????索引和范围为访问序列中的单个元素或范围提供了简洁的语法。依赖于两个新类型和两个新运算符。
System.Index 表示一个序列索引。
来自末尾运算符 ^ 的索引,指定一个索引与序列末尾相关。
System.Range 表示序列的子范围。
范围运算符 ..,用于将范围的开头和末尾指定为其操作数。
System.Index 表示一个序列索引。
来自末尾运算符 ^ 的索引,指定一个索引与序列末尾相关。
????????考虑数组 sequence。 0 索引与 sequence[0] 相同。 ^0 索引与 sequence[sequence.Length] 相同。 表达式 sequence[^0] 会引发异常,就像 sequence[sequence.Length] 一样。 对于任何数字 n,索引 ^n 与 sequence.Length - n 相同。
string[] words = [
// index from start index from end
"The", // 0 ^9
"quick", // 1 ^8
"brown", // 2 ^7
"fox", // 3 ^6
"jumps", // 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]}");
System.Range 表示序列的子范围。
范围运算符 ..,用于将范围的开头和末尾指定为其操作数。
????????范围指定范围的开始和末尾。 包括此范围的开始,但不包括此范围的末尾,这表示此范围包含开始但不包含末尾 。 范围 [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[] quickBrownFox = words[1..4];
foreach (var word in quickBrownFox)
Console.Write($"< {word} >");
Console.WriteLine();
//示例为开始和/或结束创建了开放范围:
string[] allWords = words[..]; // 包括从"The"到"dog"的全部.
string[] firstPhrase = words[..4]; // 包括从"The"到"fox"
string[] lastPhrase = words[6..]; // 包括"the"到末尾
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();
????????不仅数组支持索引和范围。 还可以将索引和范围用于 string、Span<T> 或 ReadOnlySpan<T>。
//索引和范围运算符
//AsSpan()
namespace ConsoleApp11
{
internal class Program
{
private static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
int[] numbers = [.. Enumerable.Range(0, 100)];
int x = 12;
int y = 25;
int z = 36;
Console.WriteLine($"{numbers[^x]} is the same as {numbers[^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.AsSpan()[x..y];
Span<int> y_z = numbers.AsSpan()[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.AsSpan()[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.AsSpan()[..x];
Span<int> zero_x = numbers.AsSpan()[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.AsSpan()[z..];
Span<int> z_zero = numbers.AsSpan()[z..^0];
Console.WriteLine($"\t{z_end[0]}..{z_end[^1]} is the same as {z_zero[0]}..{z_zero[^1]}");
}
}
}
//运行结果:
/*
88 is the same as 88
13 is the same as 13
numbers[x..y] and numbers[y..z] are consecutive and disjoint:
numbers[x..y] is 12 through 24, numbers[y..z] is 25 through 35
numbers[x..^x] removes x elements at each end:
numbers[x..^x] starts with 12 and ends with 87
numbers[..x] means numbers[0..x] and numbers[x..] means numbers[x..^0]
0..11 is the same as 0..11
36..99 is the same as 36..99
*/
????????使用范围运算符表达式语法时,编译器会将开始值和结束值隐式转换为 Index,并根据这些值创建新的 Range 实例。
//以下代码显示了范围运算符表达式语法的隐式转换示例及其对应的显式替代方法:
//当值为负数时,从 Int32 隐式转换为 Index 会引发 ArgumentOutOfRangeException。
//同样,当 value 参数为负时,Index 构造函数会引发 ArgumentOutOfRangeException。
namespace ConsoleApp12
{
internal class Program
{
private static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
Range implicitRange = 3..^5;
Range explicitRange = new(
start: new Index(value: 3, fromEnd: false),
end: new Index(value: 5, fromEnd: true));
if (implicitRange.Equals(explicitRange))
{
Console.WriteLine(
$"The implicit range '{implicitRange}' equals the explicit range '{explicitRange}'");
}
}
}
}
// Sample output:
/*
The implicit range '3..^5' equals the explicit range '3..^5'
*/
????????以下 .NET 类型同时支持索引和范围:String、Span<T> 和 ReadOnlySpan<T>。 List<T> 支持索引,但不支持范围。
????????单个维度数组同时支持索引和范围。 多维数组不支持索引器或范围。 多维数组的索引器具有多个参数,而不是一个参数。 交错数组(也称为数组的数组)同时支持范围和索引器。
// 交错数组,循环访问
// 演示如何循环访问交错数组的矩形子[行]。
// 它循环访问位于中心的[行],不包括前三行和后三行,
// 以及每个选定行中的前两列和后两列
namespace ConsoleApp13
{
internal class Program
{
private static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
int[][] jagged =
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10,11,12,13,14,15,16,17,18,19],
[20,21,22,23,24,25,26,27,28,29],
[30,31,32,33,34,35,36,37,38,39],
[40,41,42,43,44,45,46,47,48,49],
[50,51,52,53,54,55,56,57,58,59],
[60,61,62,63,64,65,66,67,68,69],
[70,71,72,73,74,75,76,77,78,79],
[80,81,82,83,84,85,86,87,88,89],
[90,91,92,93,94,95,96,97,98,99],
];
var selectedRows = jagged[3..^3]; //不包括前3行和后3行
foreach (var row in selectedRows)
{
var selectedColumns = row[2..^2];//遍历选中的行,范围[2..^2]
foreach (var cell in selectedColumns)
{
Console.Write($"{cell}, ");
}
Console.WriteLine();
}
}
}
}
//运行结果:
/*
32, 33, 34, 35, 36, 37,
42, 43, 44, 45, 46, 47,
52, 53, 54, 55, 56, 57,
62, 63, 64, 65, 66, 67,
*/
????????要分析较大序列的一部分时,通常会使用范围和索引。
// 范围和索引综合应用
namespace ConsoleApp14
{
internal class Program
{
private static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
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, max, average) = MovingAverage(sequence, r);
Console.WriteLine($"From {r.Start} to {r.End}: \tMin: {min},\tMax: {max},\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))];
}
}
}
//运行结果:
/*
From 0 to 10: Min: 0, Max: 300, Average: 192.7
From 100 to 110: Min: 1000, Max: 1044, Average: 1021.6
From 200 to 210: Min: 1414, Max: 1445, Average: 1429.5
From 300 to 310: Min: 1732, Max: 1757, Average: 1744.4
From 400 to 410: Min: 2000, Max: 2022, Average: 2010.6
From 500 to 510: Min: 2236, Max: 2256, Average: 2245.6
From 600 to 610: Min: 2449, Max: 2467, Average: 2458
From 700 to 710: Min: 2645, Max: 2662, Average: 2653.7
From 800 to 810: Min: 2828, Max: 2844, Average: 2835.9
From 900 to 910: Min: 3000, Max: 3014, Average: 3006.9
From ^10 to ^0: Min: 3146, Max: 3160, Average: 3153.1
From ^110 to ^100: Min: 2983, Max: 2998, Average: 2990.2
From ^210 to ^200: Min: 2810, Max: 2826, Average: 2818.2
From ^310 to ^300: Min: 2626, Max: 2643, Average: 2634.8
From ^410 to ^400: Min: 2428, Max: 2447, Average: 2437.9
From ^510 to ^500: Min: 2213, Max: 2233, Average: 2223.2
From ^610 to ^600: Min: 1974, Max: 1997, Average: 1985.5
From ^710 to ^700: Min: 1702, Max: 1729, Average: 1715.5
From ^810 to ^800: Min: 1378, Max: 1410, Average: 1394.2
From ^910 to ^900: Min: 948, Max: 994, Average: 971.3
*/
????????从数组中获取范围时,结果是从初始数组复制的数组,而不是引用的数组。 修改生成的数组中的值不会更改初始数组中的值。
// 从数组中获取范围时,结果是从初始数组复制的数组,而不是引用的数组。
// 修改生成的数组中的值不会更改初始数组中的值。
namespace ConsoleApp15
{
internal class Program
{
private static void Main(string[] args)
{
ArgumentNullException.ThrowIfNull(args);
var arrayOfFiveItems = new[] { 1, 2, 3, 4, 5 };
var firstThreeItems = arrayOfFiveItems[..3]; // 包括前3个
firstThreeItems[0] = 11; // 修改序号0的元素为11
Console.WriteLine(string.Join(",", firstThreeItems));
Console.WriteLine(string.Join(",", arrayOfFiveItems));
}
}
}
// 运行结果:
/*
11,2,3
1,2,3,4,5
*/