「C#点滴」 更加优雅地访问序列数据:索引和范围


在 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;
         }
     }
 }

隐式支持索引

满足以下条件,编译器自动添加索引支持。

  • 具有一个返回 int 值的 Length 或者 Count 属性
  • 具有可访问的实例索引器,该索引器采用单个 int 作为参数
  • 没有显示实现索引器
 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];
         }
     }
 }

Range 隐式支持

满足以下条件,编译器自动添加索引支持。

  • 具有一个返回 int 值的 Length 或者 Count 属性
  • 具有可访问方法 Slice ,它具有两个类型为 int 的参数
  • 没有显示实现范围
 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 条评论) “”
   
验证码:

相关文章

推荐文章