翻译自 John Demetriou 2020年4月6日 的文章 《C# 8 Is Introducing Ranges》

我们之前讨论过的 C# 中的一个特性 System.Index 结构体,与另一特性 System.Range 结构体 是携手并进的。

在 C# 8.0 之前,没有访问集合中任意部分的范围或切片的语法。开发人员通常不得不执行一些自定义的切片操作,或者依靠诸如 array.Skip(5).Take(2); 的 LINQ 方法。

现在 C# 添加了一个新的小特性,它可以帮助人们从一个大的数组项目中获取一个小范围的项目。

让我们来考虑一下。假设我们有一个项目数组,我们仅需展示其中的一部分。通常情况下,我们只需要循环这部分,然后像下面这样输出它们。

var array = new string[]
{
"Item0",
"Item1",
"Item2",
"Item3",
"Item4",
"Item5",
"Item6",
"Item7",
"Item8",
"Item9"
}; for (int i = 1; i <= 5; i++)
{
Console.WriteLine(array[i]);
}

因此当我们仅需要几个特定的项,我们通过控制索引值来获取它们。

通过使用范围(Range),我们可以用以下方式来替换:

var array = new string[]
{
"Item0",
"Item1",
"Item2",
"Item3",
"Item4",
"Item5",
"Item6",
"Item7",
"Item8",
"Item9"
}; foreach (var item in array[1..5])
{
Console.WriteLine(item);
}

在这里,第一次使用的人会注意到一个奇怪的情况——它只会打印四项。为什么呢?这是因为范围(Range)包含开始,但不包含末尾

如果想要与上面的 for 循环有相同的结果,我们的 foreach 循环应该是:

foreach (var item in array[1..6])
{
Console.WriteLine(item);
}

Range 的一些示例

1、从索引 1(包含) 到末尾(包含)

foreach (var item in array[1..])
{
Console.WriteLine(item);
}

2、从开始(包含)到索引 3(不包含)

foreach (var item in array[..3])
{
Console.WriteLine(item);
}

3、结合以上两种,您可以得到一个语法上正确的以下版本

foreach (var item in array[..])
{
Console.WriteLine(item);
}

它的意思是获取整个范围。

译者注

它也等同于下面两种写法:

foreach (var item in array[0..^0])
{
Console.WriteLine(item);
}
foreach (var item in array[0..array.Length])
{
Console.WriteLine(item);
}

4、从索引 1(包含)到 从末尾开始的索引 1(不包含)

foreach (var item in array[1..^1])
{
Console.WriteLine(item);
}

在这里,它结合了前面文章中介绍的 Index 的 Hat 运算符(^)。

一个快速的解释,Hat 运算符(^),会给您一个特定的索引。如果您写 ^1,也就是在请求最后一项的索引。如上所述,考虑到最后一个索引号是排除的,在一个有 10 项的序列中,请求索引项直到 ^1,您也就是在请求索引项直到索引 9(索引从 0 开始),并且该项不包括在其中。

我希望我讲清楚了。

将范围作为变量

我们前面所做的是,将范围(Range)作为类型传递。我们也可以将范围声明为一个变量:

Range range = 1..9;

然后,可以在 [] 字符中使用该范围:

var subarray = array[range];

我们还可以将其作为方法的参数传递、将其存储为私有变量,或者用在任何业务逻辑所需的地方。

范围的类型支持

范围(Range)不能被用在 List 或者 IEnumerable<Τ>,但数组并不是唯一支持它的类型。

您还可以将索引(Index)和范围(Range) 与 StringSpan<T>ReadOnlySpan<T> 一起使用。

与字符串一起使用的例子基本上就是代替 Substring 方法,如下所示:

string s = "01234567";
string r = s[1..3]; // r 将会是 "12"

对于每个担心 List(它可能是您通常用来替代数组的类型)不支持范围的人来说,List 从 .Net Framework 2.0 开始就已经有 GetRange 方法了。


以下为译者补充

索引和范围总结

索引(Index)和范围(Range)为访问序列中的单个元素或范围提供了简洁的语法。

对索引和范围的语言支持依赖于两个新类型两个新运算符

  • System.Index 类型表示一个序列索引。
  • Hat 运算符(^),指定一个索引与序列末尾相关,用于构造 System.Index 对象。
  • System.Range 类型表示序列的子范围。
  • 范围运算符(..),用于指定范围的开始和末尾,就像操作数一样,用于构造 System.Range 对象。

考虑数组 sequence0 索引与 sequence[0] 相同。 ^0 索引与 sequence[sequence.Length] 相同。 请注意,sequence[..^0] 不会引发异常,就像 sequence[..sequence.Length] 一样。对于任意数字 n,索引 ^nsequence.Length - n 相同。 如下面代码中的注释所示:

string[] words = new string[]
{
// index from start index from end
"The", // 0 ^9
"quick", // 1 ^8
"brown", // 2 ^7
"fox", // 3 ^6
"jumped", // 4 ^5
"over", // 5 ^4
"the", // 6 ^3
"lazy", // 7 ^2
"dog" // 8 ^1
}; // 9 (or words.Length) ^0

一个范围指定了范围的“开始”“末尾”。 范围是左闭右开的,也就是说范围包含“开始”,不包含“末尾”

范围 [0..sequence.Length][0..^0][..] 都表示整个范围。

作者 : John Demetriou

译者 : 技术译民

出品 : 技术译站

链接 : 英文原文

C# 中 System.Range 结构体的更多相关文章

  1. C# 中 System.Index 结构体和 Hat 运算符(^)的全新用法

    翻译自 John Demetriou 2019年2月17日 的文章 <C# 8 – Introducing Index Struct And A Brand New Usage For The ...

  2. 剔除list中相同的结构体数据

    剔除list中相同的结构体数据,有三个思路:1.两层循环,逐个比较 2.使用set容器来剔除 3.使用unique方法去重 // deduplication.cpp : 定义控制台应用程序的入口点. ...

  3. MFC中的NMHDR结构体和NMUPDOWN结构体

    建立spin控件,创建UDN_DELTAPOS一个消息函数后: void CSpinDlg::OnDeltaposSpin1(NMHDR* pNMHDR, LRESULT* pResult) { NM ...

  4. C语言中 不定义结构体变量求成员大小

    所谓的求成员大小, 是求成员在该结构体中 用 sizeof(结构体名.结构体成员名) 求来的. 很多时候我们需要知道一个结构体成员中的某个成员的大小, 但是我们又不需要定义该结构体类型的变量(定义的话 ...

  5. 如何系统学习C 语言(中)之 结构体篇

    1,结构体 在前面我们知道变量和数组都可以用来存储数据,变量用来存储单个数据,数组可以用来存储一组同类型的数据,但你有没有发现--它们都只适合单一属性的数据.那现实生活中,很多对象都是具有多属性的.例 ...

  6. 黑马程序员——Foundation中的OC结构体

    <span style="font-size:14px">------<a target="_blank" href="http:/ ...

  7. 【2016-08-18】转载:总结C++中几种结构体初始化的方法

    作者:Ac_Von 博客地址:http://www.cnblogs.com/vongang/ 文章地址:http://www.cnblogs.com/vongang/archive/2011/07/3 ...

  8. Objective-C中常用的结构体NSRange,NSPoint,NSSize(CGSize),NSRect

    本节要点:红色标记 需要记下来 1   NSRange typedef struct _NSRange {     NSUInteger location;     NSUInteger length ...

  9. f2fs解析(七)node管理器中的 free_nid 结构体

    除了node_info之外, node管理器中还有还有个重要的数据结构: struct free_nid { struct list_head list; /* for free node id li ...

随机推荐

  1. 搭建lnmp环境,nginx的配置文件/etc/nginx/nginx.conf

    #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #erro ...

  2. Spring JPA 拓展

    Spring JPA 拓展 翻译:Spring Data Extensions 本节记录了一组Spring数据扩展,它们支持在各种上下文中使用Spring数据.目前,大部分集成都是针对Spring M ...

  3. Java8 Functional(函数式接口)

    Functional 函数式(Functional)接口 只包含一个抽象方法的接口,称为函数式接口. 你可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即 ...

  4. leetcode刷题-52N皇后2

    题目 n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击.给定一个整数 n,返回 n 皇后不同的解决方案的数量. 思路 与51题完全一致 实现 class ...

  5. 采用GitOps的11大原因

    Kubernetes允许我们单纯地使用声明性的配置文件来管理我们的应用部署和其他基础设施组件(例如,我们现在都是YAML开发者).这使我们能够把所有这些文件放到Git仓库中,然后把它挂到流水线上(Je ...

  6. Shader 001 - 函数造型能力

    0x00 从函数出发 Shader 中的很多效果都是由函数计算得出的,如何更好地理解二者的关系呢.不妨先看看函数是什么?函数的定义可以简单地描述为:给定一个集合 A,对于其中的元素施加法则 f,则可以 ...

  7. 深入理解xLua热更新原理

    热更新简介 热更新是指在不需要重新编译打包游戏的情况下,在线更新游戏中的一些非核心代码和资源,比如活动运营和打补丁.热更新分为资源热更新和代码热更新两种,代码热更新实际上也是把代码当成资源的一种热更新 ...

  8. Linux打包压缩解压工具

    第1章      Linux 打包压缩解压工具一.压缩.解压工具 compress/uncompress gzip/gunzip bzip2/bunzip2/ bzcat xz/unxz/ xzcat ...

  9. [Java并发编程之美]第1章 线程基础

    第1章 线程 1.1 线程与进程 进程是操作系统资源分配和调度的基本单位,但cpu资源是分配到线程的,也就是线程是CPU分配的基本单位. 线程自己的栈资源中,存放的局部变量是线程私有的,其他线程无法访 ...

  10. vue入门(一)----工程vue_sell

    1.利用vue脚手架工具创建vue,webpack工程目录 npm install -g vue-cli  安装vue脚手架 vue init webpack sell 初始化一个webpack模板, ...