Skip to content

Commit 5f19f2d

Browse files
RexJaeschkeBillWagner
authored andcommitted
add indexers and ranges
1 parent 954d3c3 commit 5f19f2d

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

standard/classes.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3085,6 +3085,8 @@ Even though the syntax for accessing a property is the same as that for a field,
30853085

30863086
When a property declaration includes an `extern` modifier, the property is said to be an ***external property***. Because an external property declaration provides no actual implementation, each of its *accessor_declarations* consists of a semicolon.
30873087

3088+
A type is ***countable*** if it has a property named `Length` or `Count` with an accessible `get` accessor ([§14.7.3]( classes.md#1473-accessors)) and a return type of `int`.
3089+
30883090
### 14.7.2 Static and instance properties
30893091

30903092
When a property declaration includes a `static` modifier, the property is said to be a ***static property***. When no `static` modifier is present, the property is said to be an ***instance property***.
@@ -5188,3 +5190,128 @@ When the body of the async function terminates, the return task is moved out of
51885190
If the return type of the async function is `void`, evaluation differs from the above in the following way: Because no task is returned, the function instead communicates completion and exceptions to the current threads ***synchronization context***. The exact definition of synchronization context is implementation-dependent, but is a representation ofwherethe current thread is running. The synchronization context is notified when evaluation of a `void`-returning async function commences, completes successfully, or causes an uncaught exception to be thrown.
51895191
51905192
This allows the context to keep track of how many `void`-returning async functions are running under it, and to decide how to propagate exceptions coming out of them.
5193+
5194+
## §indexable-sequence Indexable sequences
5195+
5196+
### §indexable-sequence-general General
5197+
5198+
> Note to TG2 reviewers: Rationale for the choice of the name of the term *indexable sequence*: Various MS-hosted on-line pages use the term *sequence*. This word is already used quite a bit in the C# spec, in both a general sense as well as being defined in the context of query expressions. [§11.17 Query expressions|§11.17.1 General states: A query expression begins with a `from` clause and ends with either a `select` or `group` clause. The initial `from` clause may be followed by zero or more `from`, `let`, `where`, `join` or `orderby` clauses. Each `from` clause is a generator introducing a ***range variable*** that ranges over the elements of a ***sequence***. Each `let` clause introduces a range variable representing a value computed by means of previous range variables. …]. That definition is *not* applicable to indexes and ranges! The MS-provided proposal uses *collection*; however, that implies enumerable support, which is *not* required by indexes and ranges. (BTW, although it is used a lot in the C# spec, the term *collection* is *not* defined!) As such, rather than overload an existing term or invent a completely different one, I came up with *indexable sequence*.
5199+
5200+
An ***indexable sequence*** is an ordered set of zero or more elements having the same type. Any given element can be accessed via an index, and a contiguous subset of elementsreferred to as a ***slice***can be denoted via a range.
5201+
5202+
An index is represented by a read-only variable of the value type `System.Index`. A range is represented by a read-only variable of value type `System.Range`, which contains a start and end index. A slice of an array is represented by a (possibly empty) array. The representation of a slice of a user-defined type is determined by the implementer of that type.
5203+
5204+
For an indexable sequence of length *N*, elements can be accessed using indexes 0 through *N-1*, which are relative to the start. Elements can also be accessed relative to the end via the `^` index-from-end operatorindex-from-end-operator). `^0` denotes the (non-existent) element just beyond the end.
5205+
5206+
A slice can be obtained using the `..` range operatorrange-operator). A range of the form `s..e` starts at element `s` and ends with the element immediately prior to element `e`.
5207+
5208+
All single-dimensional and jagged arrays ([§16.1](arrays.md#161-general)) are indexable sequences; multi-dimensional arrays are not! The use of indexes and ranges with arrays is described in [§11.7.10.2](expressions.md#117102-array-access).
5209+
5210+
An object of type `string` is an indexable sequence.
5211+
5212+
A user-defined type can provide explicit support for indexer access ([§11.7.10.3](expressions.md#117103-indexer-access)) using `System.Index` and `System.Range`. (See §indexable-sequence-expl-support-for-index and §indexable-sequence-expl-support-for-range.) If various criteria are met, an existing user-defined type that does *not* have such explicit support, shall have provided for it by the implementation implicit support for such indexer and range access. (See §indexable-sequence-impl-support-for-index and §indexable-sequence-impl-support-for-range.) In both cases, the type is recognized as being an indexable sequence type.
5213+
5214+
### §indexable-sequence-support-for-index Providing support for Index
5215+
5216+
#### §indexable-sequence-expl-support-for-index Explicit Index support
5217+
5218+
A type having an instance indexer taking a single argument of type `System.Index`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access).
5219+
5220+
> *Example*: In [§14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Individual bits are accessed for read/write via an `int` indexer. Adding an `Index` indexer that simply interprets the `Index` argument as an `int`, is simple:
5221+
> ```csharp
5222+
> class BitArray
5223+
> {
5224+
> …
5225+
> public int Length { … }
5226+
> public bool this[int index] { … }
5227+
> public bool this[Index idx]
5228+
> {
5229+
> get
5230+
> {
5231+
> return this[idx.GetOffset(Length)]; // use the [int] indexer
5232+
> }
5233+
> set
5234+
> {
5235+
> this[idx.GetOffset(Length)] = value; // use the [int] indexer
5236+
> }
5237+
> }
5238+
> }
5239+
> ```
5240+
> *end example*
5241+
5242+
#### §indexable-sequence-impl-support-for-index Implicit Index support
5243+
5244+
An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Index` for any type that meets the following criteria:
5245+
- The type is countable [§14.7.1](classes.md#1471-general).
5246+
- The type has an accessible instance indexer taking an argument of type `int` as its only argument, or as its first argument with the remaining arguments being optional.
5247+
- The type does not have an accessible instance indexer taking a `System.Index` as its only argument, or as its first argument with the remaining arguments being optional.
5248+
5249+
The provided instance indexer shall have the same get and set members with matching accessibility as the `int` indexer.
5250+
5251+
The provided instance indexer shall take the given `System.Index` and use that to call the instance indexer taking an `int`. If both the `Length` and `Count` properties exist and are accessible, `Length` is used.
5252+
5253+
> *Note*: See §indexable-sequence-expl-support-for-index for an example of an explicitly provided `Index` indexer. If that were not defined, its equivalent would be provided by the implementation. *end note*
5254+
5255+
### §indexable-sequence-support-for-range Providing support for Range
5256+
5257+
#### §indexable-sequence-expl-support-for-range Explicit Range support
5258+
5259+
A type having an instance indexer taking a single argument of type `System.Range`, or a first argument of that type followed by optional arguments, may be indexed as described by [§11.7.10.3](expressions.md#117103-indexer-access).
5260+
5261+
> *Example*: In14.9](classes.md#149-indexers), there is an example defining type `BitArray`, which stores bits in an array of `int`. Adding a `Range` indexer that returns a `BitArray` representing the bit slice designated by the Range, is simple:
5262+
> ```csharp
5263+
> class BitArray
5264+
> {
5265+
> …
5266+
> public BitArray(int length) { … }
5267+
> public int Length { … }
5268+
> public bool this[int index] { … }
5269+
> public BitArray this[Range range] // note the return type
5270+
> {
5271+
> get
5272+
> {
5273+
> int startIdx = range.Start.GetOffset(Length);
5274+
> int endIdx = range.End.GetOffset(Length);
5275+
> int rangeLength = endIdx - startIdx;
5276+
> BitArray newBitArray = new BitArray(rangeLength);
5277+
> for (int i = startIdx; i < endIdx; ++i)
5278+
> {
5279+
> newBitArray[i - startIdx] = this[i];
5280+
> }
5281+
> return newBitArray;
5282+
> }
5283+
> }
5284+
> }
5285+
> ```
5286+
> *end example*
5287+
5288+
#### §indexable-sequence-impl-support-for-range Implicit Range support
5289+
5290+
An implementation shall behave as if it provides an instance indexer member with a single parameter of type `System.Range` for any type that meets the following criteria:
5291+
- The type is countable [§14.7.1](classes.md#1471-general).
5292+
- The type has an accessible instance method named `Slice` taking two arguments of type `int` as the only arguments. For type `string`, the method `Substring` is used instead of `Slice`.
5293+
> *Note*: As specified in11.7.10.2](expressions.md#117102-array-access), for array access, the method `System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray` is used instead of `Slice`. *end note*
5294+
- The type does not have an accessible instance indexer taking a `System.Range` as its only argument, or as its first argument with the remaining arguments being optional.
5295+
5296+
The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`.
5297+
5298+
When the type is indexed with a `System.Range`, the provided instance indexer shall take the given range and pass its start index and length as `int`s to `Slice` (or in the case of `string`, to method `Substring`).
5299+
5300+
> Note to TG2 reviewers: The MS proposal, sectionImplicit Range support,” provided a very detailed discussion of how to transform a pair of Indexes into a call to `Slice` depending on the form of the range used. Rex did *not* retain this in the final proposal, as he saw no point in doing so. Given a start and end index, it is a simple matter to compute the length **in all cases regardless of range format!**, as he shows in his range indexer implementation inExplicit range supportabove.
5301+
5302+
> *Note*: See §indexable-sequence-expl-support-for-range for an example of an explicitly provided `Range` indexer. If that were not defined, its equivalent would be provided by the implementation, except that the provided indexer would call `Slice` to create and copy the slice. For type `BitArray`, `Slice` might be defined, as follows:
5303+
> ```csharp
5304+
> public BitArray Slice(int startIdx, int rangeLength)
5305+
> {
5306+
> int endIdx = startIdx + rangeLength;
5307+
> BitArray newBitArray = new BitArray(rangeLength);
5308+
> for (int i = startIdx; i < endIdx; ++i)
5309+
> {
5310+
> newBitArray[i - startIdx] = this[i];
5311+
> }
5312+
> return newBitArray;
5313+
> }
5314+
> ```
5315+
*end note*
5316+
5317+
> Note to TG2 reviewers: Setter: What if anything should we say about implicit and explicit setter for a Range indexer? Certainly, one can define a setter for a user-defined type; however, it is not obvious as to what such a setter would do, especially since it must be used on the left-hand side of assignment taking a right-hand side of the same type as the index returns. In the case of type `BitArray` that would mean something like `ba1[range1] = ba2`, or perhaps `ba1[range1] = ba2[range2]`. As far as Rex could determine, the operations one might like to implement using such a setter are probably best implemented via a named method. In any event, for a compiler-generated Range indexer, attempting to use its setter results in the error messageCS0131 The left-hand side of an assignment must be a variable, property or indexer,” which suggests the generated indexer **has no setter**. If that is the case, we should say that in the previous section.

0 commit comments

Comments
 (0)