查看说明: 此新子句(目前(§18)暂时放置在此处,以避免由于在 PR 不受影响章节和子句中重新编号而发生文本更改。 尚未确定其最终位置,但在数组(§17)和接口(§19)章节之间可能适用 - 可以在评审期间建议其他位置。 稍后只需对它进行简单的编辑
clauses.json即可重新定位。
18.1 常规
此子句为构建的 扩展可索引 和 可切片集合 类型引入了模型:
- 此子句
System.Index(§18.2)和System.Range(§18.3)中引入的类型: - 预定义的一元
^运算符(§12.9.6)和二进制..(§12.10)运算符;和 - element_access表达式。
在模型中,类型被归类为:
- 如果 集合 表示一组 元素,则为集合
- 如果 扩展的可索引 集合支持 element_access 表达式,该表达式具有返回和/或设置类型单个元素的
Index单个参数表达式(按值或引用);以及 - 如果 扩展的可切片 集合支持 element_access 表达式,该表达式具有一个类型
Range参数表达式,该表达式按值返回类型元素的 切片 。
注意:模型不需要可以设置类型的切片,但类型可能支持它作为模型的扩展。 尾注
单维数组(§12.8.12.2)和字符串(§12.8.12.3)支持模型。
该模型可由任何类、结构或接口类型支持,该类型提供实现模型语义的相应索引器(§15.9)。
为不直接支持模型但提供特定 成员模式 (§18.4)的类型提供隐式支持。 此支持是基于模式的,而不是基于语义的,因为假定其所 基于 的类型成员的语义 – 该语言不强制或检查这些类型成员的语义。
出于此子句的目的,定义了以下术语:
- 集合是表示一组元素的类型。
-
可计数集合是一个可计数属性
int,它提供一个 -valued 实例属性,其值是组中当前元素的数目。 此属性应命名为或LengthCount。 如果两者都存在,则选择前者。 -
序列或可索引类型是集合:
- 这是可计数的;
- 如果每个元素都可以使用具有单个必需
int参数的element_access表达式进行访问,则允许从头开始索引访问其他可选参数; - 如果也可以使用element_access表达式设置每个元素,则序列是可修改的;
- 元素的 from-start 索引是序列中元素之前的元素数,对于包含 N 个元素的序列:
- 第一个元素和最后一个元素的索引分别为 0 和 N-1,以及
- 过去结束索引(表示最后一个索引之后的假设元素)具有值 N。
- 从端索引表示相对于过去结束索引的序列中元素的位置。 对于包含 N 元素的序列,第一个、最后一个和过去结束的索引分别为 N、1 和 0。
- 范围是从序列中的任何索引处开始的连续运行,即零个或多个索引。
- 切片是区域内元素的集合。
-
可切片集合是一个:
- 可计数;
- 提供一个方法
Slice,该方法采用两int个参数来指定一个范围,分别作为起始索引和元素计数,并返回从区域中的元素构造的新切片。
上述定义已扩展供使用 Index , Range 如下所示:
- 如果支持采用单个必需
Index参数而不是参数的element_access表达式,则类型也是一个int序列。 在需要区分的情况下,类型称为 可扩展索引。 - 如果采用单个必需
Range参数(而不是Slice方法)的element_access表达式,则类型也可以切片。 如果需要区分,类型称为 扩展切片。
类型是否被归类为可计数、可索引或可切片,都受成员可访问性(§7.5)的约束,因此取决于所使用的类型。
示例:一种类型,其中可计数属性和/或索引器
protected只是其自身成员和任何派生类型的序列。 示例结束
要限定为序列或可切片的类型所需的成员可以继承。
示例:在以下代码中
public class A { public int Length { get { … } } } public class B : A { public int this(int index) { … } } public class C : B { public int[] Slice(int index, int count) { … } }类型
A可计数,B是一个序列,可C切片,也是一个序列。示例结束
注意:
- 由于缺少(可访问)索引器,类型可以切片,而无需编制索引。
- 要使类型可切片和/或可索引,需要类型可计数。
- 虽然序列的元素按序列中 的位置 进行排序,但元素本身不需要按其值进行排序,甚至按可排序。
尾注
18.2 索引类型
该 System.Index 类型表示一个 抽象 索引,它是 从头索引 或 从端索引。
public readonly struct Index : IEquatable<Index>
{
public int Value { get; }
public bool IsFromEnd { get; }
public Index(int value, bool fromEnd = false);
public static implicit operator Index(int value);
public int GetOffset(int length);
public bool Equals(Index other);
}
Index 值是从一个 int构造的,指定非负偏移量,以及一个 bool,指示偏移量是来自结束(true)还是开始(false)。 如果指定的偏移量为负 ArgumentOutOfRangeException 值,则会引发。
示例
Index first = new Index(0, false); // first element index var last = new Index(1, true); // last element index var past = new Index(0, true); // past-end index Index invalid = new Index(-1); // throws ArgumentOutOfRangeException示例结束
从中生成自开始索引的隐式转换intIndex,以及从其生成自结束索引的语言定义的一元运算符^(§12.9.6)。intIndex
示例
可以使用隐式转换和一元
^运算符编写上述示例:Index first = 0; // first element index var last = ^1; // last element index var past = ^0; // past-end index示例结束
该方法GetOffset从抽象Index值转换为指定length序列的具体int索引值。 如果值为
此方法不检查返回值是否在有效范围内0(含)。length-1
注意: 未指定检查,因为预期使用结果将索引到具有
length元素的序列中,并且索引作应执行适当的检查。 尾注
Index
IEquatable<Index>根据抽象值可以比较实现和值是否相等;Index如果两个值相等且仅当各自的Value和IsFromEnd属性相等时,两个值是相等的。 但是 Index ,不会对值进行排序,也没有提供其他比较作。
注意:
Index值是无序的,因为它们是抽象索引,因此通常无法确定从结束索引之前还是之后,而不引用序列长度。 一旦转换为具体索引,例如GetOffset,这些具体索引是可比的。 尾注
Index值可以直接用于element_access表达式(§12.8.12)的argument_list,即:
- 数组访问和目标是单维数组(§12.8.12.2):
- 字符串访问 (§12.8.12.3)
- 索引器访问和目标类型具有具有类型
Index(§12.8.12.4)或值可隐式转换为的类型Index的索引器;或者 - 索引器访问和目标类型符合指定隐式
Index支持的序列模式(§18.4.2)。
18.3 范围类型
该System.Range类型表示从Start索引到索引但不包括End索引的Index抽象范围。
public readonly struct Range : IEquatable<Index>
{
public Index Start { get; }
public Index End { get; }
public Range(Index start, Index end);
public (int Offset, int Length) GetOffsetAndLength(int length);
public bool Equals(Range other);
}
Range 值是从两 Index 个值构造的。
示例
以下示例使用从 (§18.2) 和
^(§12.9.6) 运算符隐式转换intIndex为每个Range运算符创建Index值:var firstQuad = new Range(0, 4); // the indices from `0` to `3` // int values impicitly convert to `Index` var nextQuad = new Range(4, 8); // the indices from `4` to `7` var wholeSeq = new Range(0, ^0); // the indices from `0` to `N-1` where `N` is the // length of the sequence wholeSeq is used with var dropFirst = new Range(1, ^0); // the indices from `1` to `N-1` var dropLast = new Range(0, ^1); // the indices from `0` to `N-2` var maybeLast = new Range(^1, 6); // the indices from `N-1` to 5 var lastTwo = new Range(^2, ^0); // the indices from `N-2` to `N-1`示例结束
语言定义的运算符..(§12.10)从Index值创建值Range。
示例
..可以使用运算符编写上述示例:var firstQuad = 0..4; // the indices from `0` to `3` var nextQuad = 4..8; // the indices from `4` to `7` var wholeSeq = 0..^0; // the indices from `0` to `N-1` var dropFirst = 1..^0; // the indices from `1` to `N-1` var dropLast = 0..^1; // the indices from `0` to `N-2` var maybeLast = ^1..6; // the indices from `N-1` to 5 var lastTwo = ^2..^0; // the indices from `N-2` to `N-1`示例结束
作数 .. 是可选的,第一个默认 0为,第二个默认为 ^0。
示例
可以通过依赖作数的默认值来缩短上述五个示例:
var firstQuad = ..4; // the indices from `0` to `3` var wholeSeq = ..; // the indices from `0` to `N-1` var dropFirst = 1..; // the indices from `1` to `N-1` var dropLast = ..^1; // the indices from `0` to `N-2` var lastTwo = ^2..; // the indices from `N-2` to `N-1`示例结束
Range值对于长度 L有效(如果:
- 属性 L
RangeStart的具体索引,范围End为 0 到 L;并且 - 的具体索引
Start不大于具体索引End
具有参数length的方法GetOffsetAndLength将抽象Range值转换为由元组表示的具体Range值。
Range
length如果该方法无效,则ArgumentOutOfRangeException引发该方法。
返回的具体 Range 元组是一对形式 (S, N) ,其中:
-
S是范围的起始偏移量,是 属性Range的具体索引Start; -
N是范围中的项数,是具体索引End与Start属性之间的差异; - 要计算的两个值都与
length.
如果为零,则具体范围值为空N。 空混凝土范围的值可能 S 等于具体过去的索引(§18.1),非空范围可能不是。
Range当用于对集合进行切片(§18.1)的集合有效且为空时,生成的切片为空集合。
注意: 上述结果是
Range,与零相关的值有效且为空length,可用于对空集合进行切片,并导致空切片。 这不同于索引,如果集合为空,则会引发异常。 end note*
示例
将上面定义的变量与以下项结合使用
GetOffSetAndLength(6):var (ix0, len0) = firstQuad.GetOffsetAndLength(6); // ix0 = 0, len0 = 4 var (ix1, len1) = nextQuad.GetOffsetAndLength(6); // throws ArgumentOutOfRangeException // as range crosses sequence end var (ix2, len2) = wholeSeq.GetOffsetAndLength(6); // ix2 = 0, len2 = 6 var (ix3, len3) = dropFirst.GetOffsetAndLength(6); // ix3 = 1, len3 = 5 var (ix4, len4) = dropLast.GetOffsetAndLength(6); // ix4 = 0, len4 = 5 var (ix5, len5) = maybeLast.GetOffsetAndLength(6); // ix5 = 5, len5 = 1 var (ix6, len6) = lastTwo.GetOffsetAndLength(6); // ix6 = 4, len6 = 2
Range
IEquatable<Range>根据抽象值可以比较实现和值是否相等;Range如果两个值相等且仅当相应Start属性End的抽象值相等(§18.2)时,两个值是相等的。 但是 Range ,不会对值进行排序,也没有提供其他比较作。
注意:
Range值是无序的,因为它们是抽象的,并且没有唯一的排序关系。 转换为具体开始和长度后,GetOffsetAndLength可以定义排序关系。 尾注
Range值可以直接用于element_access表达式(§12.8.12)的argument_list,即:
- 数组访问和目标是单维数组(§12.8.12.2):
- 字符串访问 (§12.8.12.3):
- 索引器访问和目标类型具有具有类型
Range(§12.8.12.4)或值可隐式转换为的类型Range的索引器;或者 - 索引器访问(§12.8.12.4)和目标类型符合指定隐式
Range支持的序列模式(§18.4.3)。
18.4 基于模式的对索引和范围的隐式支持
18.4.1 常规
如果窗体E[A]的element_access表达式(§12.8.12);其中具有E类型T且A是一个隐式转换为Index或无法转换为的Range单个表达式,则无法标识为:
- 数组访问 (§12.8.12.2),
- 字符串访问(§12.8.12.3),或
- 索引器访问(§12.8.12.4),因为
T不提供合适的可访问索引器
如果符合特定模式,则提供 T 对表达式的隐式支持。 如果 T 不符合此模式,则会发生编译时错误。
18.4.2 隐式索引支持
如果在任何上下文中,element_access表达式(§12.8.12)的形式E[A];其中具有E类型T且A是一个隐式可转换为Index的表达式;无效(§18.4.1),则在同一上下文中:
-
T提供符合 序列 的可访问成员(§18.1);和 - 表达式
E[0]有效,并且使用相同的索引器来限定T为序列
然后,应隐式支持表达式 E[A] 。
如果不限制此标准的实现,表达式的计算顺序应等效于:
-
E计算结果; -
A计算结果; - 计算的可计数属性
T(如果实现需要); - 调用同一上下文中使用的基于索引
E[0]器的Tget 或 set 访问器int。
18.4.3 隐式范围支持
如果在任何上下文中,element_access表达式(§12.8.12)的形式E[A];其中具有E类型T且A是一个隐式可转换为Range的表达式;无效(§18.4.1),则在同一上下文中:
-
T提供可计数和可切片的可访问成员(§18.1)
然后,应隐式支持表达式 E[A] 。
如果不限制此标准的实现,表达式的计算顺序应等效于:
-
E计算结果; -
A计算结果; - 计算的可计数属性
T(如果实现需要); - 调用
Slice的方法T。