C# 使用 Index 和 Range 简化集合操作
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();
输出结果如下:
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 ..
) 都只是语法糖,本质上是调用 Index
、Range
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 简化集合操作相关推荐
- ListGetandSetDemo 集合操作get set remove add方法
ListGetandSetDemo 集合操作get set remove add方法 /*** 集合操作 --线性表 概念: List是Collection的子接口,用于定义线性表数据结构. * 可以 ...
- 如何在 C# 8 中使用 Index 和 Range
C# 8 中有几个比较好玩的新特性,比如下面的这两个:System.Index 和 System.Range,分别对应着索引和切片操作,这篇文章将会讨论这两个类的使用. System.Index 和 ...
- Numpy入门教程:08. 集合操作
背景 什么是 NumPy 呢? NumPy 这个词来源于两个单词 – Numerical和Python.其是一个功能强大的 Python 库,可以帮助程序员轻松地进行数值计算,通常应用于以下场景: 执 ...
- JavaScript常见集合操作
JavaScript常见集合操作 集合的遍历 FOR循环(效率最高) 优点:JavaScript最普遍的for循环,执行效率最高 缺点:无法遍历对象 for(let i=0;i<array.le ...
- python day2 python基础 列表、元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码...
本节内容 列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 ...
- Oracle 语言分类 数据类型 数据类型转换 常用函数 集合操作 子查询
SQL分类 SQL(Structure Query Language)语言是数据库的核心语言.SQL语言共分为四大类:数据定义语言DDL,数据操纵语言DML,数据查询语言DQL,数据控制语言DCL.1 ...
- 在ASP.NET Core中使用AOP来简化缓存操作
前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...
- 好用java库(二) : lambdaj (集合操作)
接着介绍另外一个好用的java库. 记得之前做过一个web services,业务逻辑是很简单,可是代码写得多又长,因为基本上都是在对ArrayList结果进行各种筛选,排序,聚合等操作.大家都有这样 ...
- [转载] 【Python】set() 集合操作与运算 元素输出顺序
参考链接: Python中set的copy 集合 | SET 集合(set)是Python中一种重要的数据类型,表示一组各不相同元素的无序集合,其主要应用于重复元素消除及关系测试等 集合在Pytho ...
最新文章
- 《中国人工智能学会通讯》——4.14 相关研究现状
- 深度学习之好的博客文章
- 《卓有成效的管理者》——读书笔记
- SAP SHD0的详细简介
- 携程Apollo动态配置日志级别
- 使用opencv的LBF算法进行人脸关键点检测
- 使用Java和Google GSON解析ESPN API
- 点阵字体显示系列补记2:关于24点阵汉字显示程序及其修改版本
- hive分析函数取最新_Hive的分析函数的学习
- 【AD18新手入门】从零开始制造自己的PCB
- 使用MATLAB进行二次规划求解最优值
- HiJson,一个json格式化查看工具
- ma2灯光控制台 linux,ma2灯光控制台简易教程新.pdf
- oracle数据库实例改名,如何修改数据库实例及数据库名
- ipad上能够编辑python_10 个可以在平板电脑上使用的 Python 编辑器
- Unity TimeLine丨A1.创建TimeLine、Animation Track,Extrapolation属性讲解
- Java使用Separator替代符号拼接
- CentOS支持中文
- Photoshop快速的制作标准一寸证件照(8张的或者9张的)
- 物理学家看人工智能:懂了你就不怕了