Skip to content

Commit 7f7753f

Browse files
RexJaeschkeBillWagner
authored andcommitted
add indexers and ranges
1 parent f56420d commit 7f7753f

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
@@ -3049,6 +3049,8 @@ Even though the syntax for accessing a property is the same as that for a field,
30493049

30503050
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.
30513051

3052+
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`.
3053+
30523054
### 14.7.2 Static and instance properties
30533055

30543056
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***.
@@ -5087,3 +5089,128 @@ When the body of the async function terminates, the return task is moved out of
50875089
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.
50885090
50895091
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.
5092+
5093+
## §indexable-sequence Indexable sequences
5094+
5095+
### §indexable-sequence-general General
5096+
5097+
> 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*.
5098+
5099+
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.
5100+
5101+
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.
5102+
5103+
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.
5104+
5105+
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`.
5106+
5107+
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).
5108+
5109+
An object of type `string` is an indexable sequence.
5110+
5111+
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.
5112+
5113+
### §indexable-sequence-support-for-index Providing support for Index
5114+
5115+
#### §indexable-sequence-expl-support-for-index Explicit Index support
5116+
5117+
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).
5118+
5119+
> *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:
5120+
> ```csharp
5121+
> class BitArray
5122+
> {
5123+
> …
5124+
> public int Length { … }
5125+
> public bool this[int index] { … }
5126+
> public bool this[Index idx]
5127+
> {
5128+
> get
5129+
> {
5130+
> return this[idx.GetOffset(Length)]; // use the [int] indexer
5131+
> }
5132+
> set
5133+
> {
5134+
> this[idx.GetOffset(Length)] = value; // use the [int] indexer
5135+
> }
5136+
> }
5137+
> }
5138+
> ```
5139+
> *end example*
5140+
5141+
#### §indexable-sequence-impl-support-for-index Implicit Index support
5142+
5143+
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:
5144+
- The type is countable [§14.7.1](classes.md#1471-general).
5145+
- 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.
5146+
- 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.
5147+
5148+
The provided instance indexer shall have the same get and set members with matching accessibility as the `int` indexer.
5149+
5150+
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.
5151+
5152+
> *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*
5153+
5154+
### §indexable-sequence-support-for-range Providing support for Range
5155+
5156+
#### §indexable-sequence-expl-support-for-range Explicit Range support
5157+
5158+
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).
5159+
5160+
> *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:
5161+
> ```csharp
5162+
> class BitArray
5163+
> {
5164+
> …
5165+
> public BitArray(int length) { … }
5166+
> public int Length { … }
5167+
> public bool this[int index] { … }
5168+
> public BitArray this[Range range] // note the return type
5169+
> {
5170+
> get
5171+
> {
5172+
> int startIdx = range.Start.GetOffset(Length);
5173+
> int endIdx = range.End.GetOffset(Length);
5174+
> int rangeLength = endIdx - startIdx;
5175+
> BitArray newBitArray = new BitArray(rangeLength);
5176+
> for (int i = startIdx; i < endIdx; ++i)
5177+
> {
5178+
> newBitArray[i - startIdx] = this[i];
5179+
> }
5180+
> return newBitArray;
5181+
> }
5182+
> }
5183+
> }
5184+
> ```
5185+
> *end example*
5186+
5187+
#### §indexable-sequence-impl-support-for-range Implicit Range support
5188+
5189+
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:
5190+
- The type is countable [§14.7.1](classes.md#1471-general).
5191+
- 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`.
5192+
> *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*
5193+
- 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.
5194+
5195+
The provided instance indexer shall have the same accessibility and return type, including `ref` if present, as `Slice`.
5196+
5197+
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`).
5198+
5199+
> 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.
5200+
5201+
> *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:
5202+
> ```csharp
5203+
> public BitArray Slice(int startIdx, int rangeLength)
5204+
> {
5205+
> int endIdx = startIdx + rangeLength;
5206+
> BitArray newBitArray = new BitArray(rangeLength);
5207+
> for (int i = startIdx; i < endIdx; ++i)
5208+
> {
5209+
> newBitArray[i - startIdx] = this[i];
5210+
> }
5211+
> return newBitArray;
5212+
> }
5213+
> ```
5214+
*end note*
5215+
5216+
> 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)