C# 使用 Index 和 Range 简化集合操作

Intro

有的语言数组的索引值是支持负数的,表示从后向前索引,比如:arr[-1]

从 C# 8 开始,C# 支持了数组的反向 Index,和 Range 操作,反向 Index 类似于其他语言中的负索引值,但其实是由编译器帮我们做了一个转换,Range 使得我们对数组截取某一部分的操作会非常简单,下面来看一下如何使用吧

Sample

使用 ^ 可以从集合的最后开始索引元素,如果从数组的最后开始索引元素,最后一个元素应该是 1 而不是0如:arr[^1]

使用 .. 可以基于某个数组截取集合中的某一段创建一个新的数组,比如 var newArray = array[1..^1],再来看一下下面的示例吧

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();

输出结果如下:

output

Index

那么它是如何实现的呢,索引值引入了一个新的数据结构 System.Index,当你使用 ^ 运算符的时候,实际转换成了 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 这种从数组最后索引的特性,只需要加一个类型是 Index 的索引器就可以了,正向索引也是支持的,int 会自动隐式转换为 Index,除了显示的增加 Index 索引器之外,还可以隐式支持,实现一个 int Count {get;} 的属性(属性名叫 Length 也可以),在实现一个 int 类型的索引器就可以了

写一个简单的小示例:

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 的基础上实现的,Range 需要两个 Index 来指定开始和结束

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 呢?

一种方式是自己直接实现一个类型是 Range 的索引器

另外一种方式是隐式实现,在自定义类中添加一个 Count 属性,然后实现一个 Slice 方法,Slice 方法有两个 int 类型的参数,第一个参数表示 offset,第二个参数表示 length

来看下面这个示例吧,还是刚才那个类,我们支持一下 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 ..) 都只是语法糖,本质上是调用 IndexRange

Index 并不是支持负数索引,从最后向前索引只是编译器帮我们做了一个转换,转换成从前到后的索引值,借助于它,我们很多取集合最后一个元素的写法就可以大大的简化了,就可以从原来的 array[array.Length-1] => array[^1]

Range 在创建某个数组的子序列的时候就非常的方便, newArray = array[1..3],需要注意的是,Range 是"左闭右开"的,包含左边界的值,不包含右边界的值

还没使用过 Index/Range 的快去体验一下吧,用它们优化数组的操作吧~~

References

  • https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/ranges-indexes

  • https://docs.microsoft.com/en-us/dotnet/api/system.index?view=net-5.0

  • https://docs.microsoft.com/en-us/dotnet/api/system.range?view=net-5.0

  • https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Index.cs

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Range.cs

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/ArraySegment.cs

C# 使用 Index 和 Range 简化集合操作相关推荐

  1. ListGetandSetDemo 集合操作get set remove add方法

    ListGetandSetDemo 集合操作get set remove add方法 /*** 集合操作 --线性表 概念: List是Collection的子接口,用于定义线性表数据结构. * 可以 ...

  2. 如何在 C# 8 中使用 Index 和 Range

    C# 8 中有几个比较好玩的新特性,比如下面的这两个:System.Index 和 System.Range,分别对应着索引和切片操作,这篇文章将会讨论这两个类的使用. System.Index 和 ...

  3. Numpy入门教程:08. 集合操作

    背景 什么是 NumPy 呢? NumPy 这个词来源于两个单词 – Numerical和Python.其是一个功能强大的 Python 库,可以帮助程序员轻松地进行数值计算,通常应用于以下场景: 执 ...

  4. JavaScript常见集合操作

    JavaScript常见集合操作 集合的遍历 FOR循环(效率最高) 优点:JavaScript最普遍的for循环,执行效率最高 缺点:无法遍历对象 for(let i=0;i<array.le ...

  5. python day2 python基础 列表、元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码...

    本节内容 列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 ...

  6. Oracle 语言分类 数据类型 数据类型转换 常用函数 集合操作 子查询

    SQL分类 SQL(Structure Query Language)语言是数据库的核心语言.SQL语言共分为四大类:数据定义语言DDL,数据操纵语言DML,数据查询语言DQL,数据控制语言DCL.1 ...

  7. 在ASP.NET Core中使用AOP来简化缓存操作

    前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...

  8. 好用java库(二) : lambdaj (集合操作)

    接着介绍另外一个好用的java库. 记得之前做过一个web services,业务逻辑是很简单,可是代码写得多又长,因为基本上都是在对ArrayList结果进行各种筛选,排序,聚合等操作.大家都有这样 ...

  9. [转载] 【Python】set() 集合操作与运算 元素输出顺序

    参考链接: Python中set的copy 集合 | SET 集合(set)是Python中一种重要的数据类型,表示一组各不相同元素的无序集合,其主要应用于重复元素消除及关系测试等  集合在Pytho ...

最新文章

  1. 《中国人工智能学会通讯》——4.14 相关研究现状
  2. 深度学习之好的博客文章
  3. 《卓有成效的管理者》——读书笔记
  4. SAP SHD0的详细简介
  5. 携程Apollo动态配置日志级别
  6. 使用opencv的LBF算法进行人脸关键点检测
  7. 使用Java和Google GSON解析ESPN API
  8. 点阵字体显示系列补记2:关于24点阵汉字显示程序及其修改版本
  9. hive分析函数取最新_Hive的分析函数的学习
  10. 【AD18新手入门】从零开始制造自己的PCB
  11. 使用MATLAB进行二次规划求解最优值
  12. HiJson,一个json格式化查看工具
  13. ma2灯光控制台 linux,ma2灯光控制台简易教程新.pdf
  14. oracle数据库实例改名,如何修改数据库实例及数据库名
  15. ipad上能够编辑python_10 个可以在平板电脑上使用的 Python 编辑器
  16. Unity TimeLine丨A1.创建TimeLine、Animation Track,Extrapolation属性讲解
  17. Java使用Separator替代符号拼接
  18. CentOS支持中文
  19. Photoshop快速的制作标准一寸证件照(8张的或者9张的)
  20. 物理学家看人工智能:懂了你就不怕了

热门文章

  1. 查看/修改Linux时区和时间
  2. Python基础学习总结__Day3
  3. git推送本地分支到远程分支
  4. java 的原码、补码、反码小总结
  5. Java异常处理教程
  6. 机器学习 vs. 深度学习
  7. Windows 查看端口占用
  8. C# 读写ACCESS的OLE对象,演示图片与长文件的读写
  9. 节省大量教科书的三种潜在风险方法
  10. windows获取本地时间_如何在Windows 8中重新获得本地登录