在 C# 8 之前,要实现诸如“访问序列的倒数第二个元素”"获取由第三个至第五个元素之间的元素组成的新序列"之类的功能是比较麻烦的,不够简洁直观。对此,C# 8 引入了索引和范围概念,极大简化了此类操作。
C# 8 引入了新类型 System.Index 用于表示序列的索引,可以直接由 int 类型自动转换赋值。
int[] fibSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
// 代表第3个元素的索引,起始索引为 0
Index index1 = 2;
// 输出: 2
Console.WriteLine(fib)
看到这儿,性急的小伙伴应该要骂娘了,这不是多此一举嘛!直接用数字索引访问不是更好?
别着急,慢慢来,我们还可以在索引前面加 ^ 从后往前索引。
int[] fibSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
// 第3个
Index index1 = 2;
// 第5个
Index index2 = 5;
// 倒数第1个
Index index3 = ^1;
// 倒数第3个
Index index4 = ^3;
// 输出:55
Console.WriteLine(fibSequence[index3]);
// 输出:21
Console.WriteLine(fibSequence[index4]);
可以看出 ^n 实际上等同于 sequence.Length - n,因为数组的最后一个元素的索引是 sequence.Length - 1,因此 n = 0 时会报错,即 ^0 访问单个元素时会报错。
嗯,现在感觉有点意思了,但是不是有点繁琐呢,还要申明一个 Index 类型的变量才能用。其实不用,我们之所以写出来,是因为强调其背后的数据类型为新引入的 System.Index,实际中直接使用字面量即可。
int[] fibSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
// 倒数第1个:55
Console.WriteLine(fibSequence[^1]);
// 倒数第2个:21
Console.WriteLine(fibSequence[^2]);
// 倒数第0个:报错
Console.WriteLine(fibSequence[^0]);
C# 8 引入了新类型 System.Range 表示序列的一部分,表达形式为 起始索引..结束索引。
int[] fibSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
// {2,3}
var subSequence1 = fibSequence[2..4];
// {3,5}
var subSequence2 = fibSequence[3..5];
// { 1, 1, 2, 3, 5, 8, 13, 21, 34 }
var subSequence3 = fibSequence[0..^1];
可以看出,结束索引不包括在结果中。2..4 实际上包含 fibSequence[2] 到 fibSequence[3]的值。以此类推, 0..^1 包含fibSequence[0] 到 fibSequence[^2]的值, 缺少最后一个元素。要表示整个范围,可以用 0..^0 。
开始索引和结束索引可以省略。
int[] fibSequence = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 };
// 等同 5..^0 :{ 8, 13, 21, 34, 55 }
var subSequence1 = fibSequence[5..];
// 等同 0..^5 : { 1, 1, 2, 3, 5 }
var subSequence2 = fibSequence[..5];
// 全部,等同于 0..^0
var subSequence3 = fibSequence[..];
索引和范围的支持不限于数组,方式包括显示支持和隐式支持。
通过实现带 Index 或者 Range 类型参数的索引器来显示支持索引或范围。
public class ExplicitIndexAndRange
{
private List _list = new();
///
/// 显示索引,通过带 Index 类型参数的索引器实现
///
public int this[Index index]
{
get
{
var idx = index.IsFromEnd ? _list.Count - index.Value : index.Value;
if (idx < 0 || idx >= _list.Count)
{
return -1;
}
return _list[idx];
}
}
///
/// 显示支持范围,通过带 Range 类型参数的索引器实现
///
public List this[Range range]
{
get
{
var sIdx = range.Start;
var eIdx = range.End;
var startIndex = sIdx.IsFromEnd ? _list.Count - sIdx.Value : sIdx.Value;
var endIndex = eIdx.IsFromEnd ? _list.Count - eIdx.Value : eIdx.Value;
var newList = new List();
for (int i = startIndex; i < endIndex; i++)
{
newList.Add(_list[i]);
}
return newList;
}
}
}
满足以下条件,编译器自动添加索引支持。
public class ImplicitIndexAndRange
{
private List _list = new();
///
/// 实现 Length 或者 Count 属性,必须返回 int 类型值
///
public int Count => _list.Count;
///
/// 实现 int 索引器
///
public int this[int index]
{
get
{
if (index < 0 || index >= Count)
{
return -1;
}
return _list[index];
}
}
}
满足以下条件,编译器自动添加索引支持。
public class ImplicitIndexAndRange
{
private List _list = new();
///
/// 实现 Length 或者 Count 属性,必须返回 int 类型值
///
public int Count => _list.Count;
///
/// Slice,省略了长度检查异常处理
///
public int[] Slice(int start, int length)
{
var slice = new int[length];
for (var i = start; i < start + length; i++)
{
slice[i] = _list[i];
}
return slice;
}
}
留言与评论(共有 0 条评论) “” |