Skip to content
11 changes: 11 additions & 0 deletions standard/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@
- `System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute` and `System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute`, which are used to declare a custom interpolated string expression handler ([§23.5.10.1](attributes.md#235101-custom-interpolated-string-expression-handlers)) and to call one of its constructors, respectively.
- System.Diagnostics.CodeAnalysis.UnscopedRefAttribute ([§23.5.8](attributes.md#2358-the-unscopedref-attribute)), which allows an otherwise implicitly scoped ref to be treated as not being scoped.
- `System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute` ([§23.5.11.1](attributes.md#235111-the-setsrequiredmembers-attribute)) and `System.Runtime.CompilerServices.RequiredMemberAttribute` ([§23.5.11.2](attributes.md#235112-the-requiredmember-attribute)), which are used in required-member contexts ([§15.7.1](classes.md#1571-general)).
- `System.Runtime.CompilerServices.CollectionBuilderAttribute` (§collection-builder-attr), which designates a collection type as having a collection-creation method.

The Nullable static analysis attributes ([§23.5.7](attributes.md#2357-code-analysis-attributes)) can improve the correctness of warnings generated for nullabilities and null states ([§8.9.5](types.md#895-nullabilities-and-null-states)).

Expand Down Expand Up @@ -1237,11 +1238,11 @@

This attribute may can be applied to any `ref` and it changes the ref-safe-context to be one level wider than its default. For example:

| UnscopedRef applied to | Original ref-safe-context | New ref-safe-context |

Check failure on line 1241 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1241:77 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

Check failure on line 1241 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1241:1 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected leading pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md
| --- | --- | --- |

Check failure on line 1242 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1242:19 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

Check failure on line 1242 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1242:1 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected leading pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md
| instance member | function-member | return-only |

Check failure on line 1243 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1243:51 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

Check failure on line 1243 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1243:1 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected leading pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md
| `in` / `ref` parameter | return-only | caller-context |

Check failure on line 1244 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1244:57 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

Check failure on line 1244 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1244:1 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected leading pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md
| `out` parameter | function-member | return-only |

Check failure on line 1245 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1245:51 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected trailing pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

Check failure on line 1245 in standard/attributes.md

View workflow job for this annotation

GitHub Actions / lint

Table pipe style

standard/attributes.md:1245:1 MD055/table-pipe-style Table pipe style [Expected: no_leading_or_trailing; Actual: leading_and_trailing; Unexpected leading pipe] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md055.md

When applying this attribute to an instance method of a struct it modifies the implicit `this` parameter; that is, `this` acts as an unannotated `ref` of the same type.

Expand Down Expand Up @@ -1562,6 +1563,16 @@

This attribute indicates that the current type has one or more required members ([§15.7.1](classes.md#1571-general)), or that a specific member of that type is required. However, it is an error for this attribute to be used explicitly. Instead, the presence of the modifier `required` results in the type or member being treated as if it were decorated with this attribute.

### §collection-builder-attr The CollectionBuilder attribute

This attribute designates a collection type as having a collection-creation method (§declaring-a-collection-type-general).

The constructor takes a builder type and the name of the method to be invoked to construct an instance of the collection type.

The attribute can be applied to a class, struct, ref struct, or interface. The attribute is not inherited although it can be applied to a base class or an abstract class.

The builder type shall be a non-generic class or struct.

## 23.6 Attributes for interoperation

For interoperation with other languages, an indexer may be implemented using indexed properties. If no `IndexerName` attribute is present for an indexer, then the name `Item` is used by default. The `IndexerName` attribute enables a developer to override this default and specify a different name.
Expand Down
157 changes: 156 additions & 1 deletion standard/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2551,6 +2551,8 @@ Except for allowing a variable number of arguments in an invocation, a parameter
> F(arr);
> F(10, 20, 30, 40);
> F();
> F([-5, 3, 9]);
> F([]);
> }
> }
> ```
Expand All @@ -2561,6 +2563,8 @@ Except for allowing a variable number of arguments in an invocation, a parameter
> Array contains 3 elements: 1 2 3
> Array contains 4 elements: 10 20 30 40
> Array contains 0 elements:
> Array contains 3 elements: -5 3 9
> Array contains 0 elements:
> ```
>
> The first invocation of `F` simply passes the array `arr` as a value parameter. The second invocation of F automatically creates a four-element `int[]` with the given element values and passes that array instance as a value parameter. Likewise, the third invocation of `F` creates a zero-element `int[]` and passes that instance as a value parameter. The second and third invocations are precisely equivalent to writing:
Expand All @@ -2570,7 +2574,7 @@ Except for allowing a variable number of arguments in an invocation, a parameter
> F(new int[] {});
> ```
>
> *end example*
> The fourth and fifth invocations pass a three-element and an empty collection expression, respectively. *end example*

When performing overload resolution, a method with a parameter array might be applicable, either in its normal form or in its expanded form ([§12.6.4.2](expressions.md#12642-applicable-function-member)). The expanded form of a method is available only if the normal form of the method is not applicable and only if an applicable method with the same signature as the expanded form is not already declared in the same type.

Expand Down Expand Up @@ -6751,3 +6755,154 @@ A positional record class ([§15.2.1](classes.md#1521-general)) with at least on
> ```
>
> *end example*

## §declaring-a-collection-type Declaring a collection type

### §declaring-a-collection-type-general General

There are a number of contexts in which a collection expression (§collection-expressions) may be converted to a collection type (§imp-collection-expression-conv). One of them is for a target class, struct, or interface type to be made a collection type by annotating it with an attribute, as shown below.

Here is a simple user-defined collection type and its associated builder type:

<!-- Example: {template:"standalone-lib", name:"CollectionType1"} -->
```csharp
[CollectionBuilder(typeof(MyCollectionBuilder), "Create")]
public class MyCollection<T> : IEnumerable<T>
{
private readonly T[] _storage;
public int Count { get; }
public T this[int index]
{
get
{
return _storage[index];
}

set
{
_storage[index] = value;
}
}
public MyCollection(ReadOnlySpan<T> elements)
{
Count = elements.Length;
_storage = new T[Count];
for (int i = 0; i < Count; i++)
{
_storage[i] = elements[i];
}
}
public IEnumerator<T> GetEnumerator() =>
_storage.AsEnumerable<T>().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() =>
_storage.GetEnumerator();
}
internal static class MyCollectionBuilder
{
internal static MyCollection<T>
Create<T>(ReadOnlySpan<T> values) =>
new MyCollection<T>(values);
}
```

The collection type shall be annotated with a `CollectionBuilder` attribute (§collection-builder-attr) that designates an associated, non-generic builder class or struct type having a collection-creation method (whose name is user-defined; in this case, it is `Create`).

The job of a ***collection-creation method*** is to create and initialize an instance of its associated collection type.

First, the set of applicable collection-creation methods `CM` is determined. It consists of methods that meet the following requirements:

- Be directly declared in the builder type
- Be static
- Be accessible at its point of use
- Its arity shall match that of the collection type
- It shall have a single parameter of type `System.ReadOnlySpan<E>`, passed by value.
- There shall be an implicit identity conversion ([§10.2.2](conversions.md#1022-identity-conversion)), an implicit reference conversion ([§10.2.8](conversions.md#1028-implicit-reference-conversions)), or a boxing conversion ([§10.2.9](conversions.md#1029-boxing-conversions)) from the method return type to the collection type.
- It shall return an instance of the collection being built, which contains a copy of the data from the span parameter.

Methods declared on base types or interfaces are ignored and not part of the `CM` set.

If the `CM` set is empty, then the collection type doesn't have an element type, doesn't have a collection-creation method, and none of the following steps apply.

If only one method among those in the `CM` set has an identity conversion from `E` to the element type of the collection type, that is the collection-creation method for the collection type. Otherwise, the collection type doesn't have a collection-creation method.

It is an error if the `CollectionBuilder` attribute does not refer to an invokable method with the expected signature.

For a *collection_expression* with a target type `C<S₀, S₁, …>` where the type declaration `C<T₀, T₁, …>` has an associated collection-creation method `B.M<U₀, U₁, …>()`, the generic type arguments from the target type are applied in order (from outermost containing type to innermost) to the collection-creation method.

The span parameter for the collection-creation method may be explicitly marked `scoped` or `[UnscopedRef] ([§9.7.3](variables.md#973-the-scoped-modifier))`. If the parameter is implicitly or explicitly `scoped`, the compiler may allocate the storage for the span on the stack rather than the heap.

The construction of an instance of a collection type is described in §collection-construction.

### §collection-construction Collection construction

The *collection_element*s of a *collection_expression* are evaluated in order, left to right. Each *collection_element* is evaluated exactly once, and any further references to the any elements refer to the results of this initial evaluation.

A *spread_element* may be iterated before or after the subsequent elements in the *collection_expression* are evaluated.

An unhandled exception thrown from any of the methods used during construction shall go uncaught and shall prevent further steps in the construction.

`Length`, `Count`, and `GetEnumerator` are assumed to have no side effects.

If the target type is a struct or class type that implements `System.Collections.IEnumerable`, and the target type does not have a collection-creation method (§declaring-a-collection-type-general), the construction of the collection instance steps are, as follows:

- The elements are evaluated in order. Some or all elements may be evaluated during the steps below rather than before.
- The compiler may determine the known length of the collection expression by invoking countable properties ([§18.1](ranges.md#181-general)) or equivalent properties from well-known interfaces or types, on each *spread_element*’s *expression*.
- The constructor that is applicable with no arguments is invoked.
- For each *collection_element*, in order:

- If the *collection_element* is an *expression_element*, the applicable `Add` instance or extension method is invoked with the *element_expression* as the argument. (Unlike classic collection initializer behavior ([§12.8.17.3.1](expressions.md#1281731-collection-initializers)), element evaluation and `Add` calls are not necessarily interleaved.)
- If the *collection_element* is a *spread_element* then one of the following steps is used:

- An applicable `GetEnumerator` instance or extension method is invoked on the *spread_element*’s *expression*, and for each item from the enumerator the applicable `Add` instance or extension method is invoked on the collection instance with the item as the argument. If the enumerator implements `IDisposable`, then `Dispose` shall be called after enumeration, regardless of any exceptions.
- An applicable `AddRange` instance or extension method is invoked on the collection instance with the *spread_element*’s *expression* as the argument.
- An applicable `CopyTo` instance or extension method is invoked on the *spread_element*’s *expression* with the collection instance and `int` index as arguments.

- During the construction steps above, an applicable `EnsureCapacity` instance or extension method may be invoked one or more times on the collection instance with an `int` capacity argument.

If the target type is an array, a `Span` or `ReadOnlySpan`, a type with a collection-creation method, or an interface, the construction steps of the collection instance are, as follows:

- The elements are evaluated in order. Some or all elements may be evaluated during the steps below rather than before.
- The compiler may determine the known length of the collection expression by invoking countable properties (or equivalent properties from well-known interfaces or types) on each *spread_element*’s *expression*.

- An *initialization instance* is created as follows:

- If the target type is an array and the collection expression has a known length, an array is allocated with the expected length.
- If the target type is a `Span` or `ReadOnlySpan`, or a type with a collection-creation method, and the collection has a known length, a `Span` or `ReadOnlySpan` with the expected length is created referring to contiguous storage.
- Otherwise, intermediate storage is allocated.

- For each *collection_element*, in order:

- If the *collection_element*is an *expression_element*, the initialization instance indexer is invoked to add the evaluated expression at the current index.
- If the element is a *spread_element* then one of the following is used:

- A member of a well-known interface or type is invoked to copy items from the spread element expression to the initialization instance.
- An applicable `GetEnumerator` instance or extension method is invoked on the *spread_element*’s *expression*, and for each item from the enumerator, the initialization instance indexer is invoked to add the item at the current index. If the enumerator implements `IDisposable`, then `Dispose` shall be called after enumeration, regardless of any exceptions.
- An applicable `CopyTo` instance or extension method is invoked on the *spread_element*’s *expression* with the initialization instance and `int` index as arguments.

- If intermediate storage was allocated for the collection, a collection instance is allocated with the actual collection length and the values from the initialization instance are copied to the collection instance, or if a span is required the compiler may use a span of the actual collection length from the intermediate storage. Otherwise, the initialization instance is the collection instance.
- If the target type has a collection-creation method, that method is invoked with the span instance.

> *Note:* The compiler might delay adding elements to the collection (or delay iterating through *spread_element*s) until after evaluating subsequent elements. (When subsequent *spread_element*s have countable properties that would allow calculating the expected length of the collection before allocating the collection.) Conversely, the compiler might eagerly add elements to the collection (and eagerly iterate through *spread_element*s) when there is no advantage to delaying.
>
> Consider the following collection expression:
>
> ```csharp
> int[] x = [a, ..b, ..c, d];
> ```
>
> If *spread_element*s `b` and `c` are countable, the compiler could delay adding items from `a` and `b` until after `c` is evaluated, to allow allocating the resulting array at the expected length. After that, the compiler could eagerly add items from `c`, before evaluating `d`, as shown below.
>
> ```csharp
> var __tmp1 = a;
> var __tmp2 = b;
> var __tmp3 = c;
> var __result = new int[2 + __tmp2.Length + __tmp3.Length];
> int __index = 0;
> __result[__index++] = __tmp1;
> foreach (var __i in __tmp2) __result[__index++] = __i;
> foreach (var __i in __tmp3) __result[__index++] = __i;
> __result[__index++] = d;
> x = __result;
> ```
>
> *end note*
46 changes: 46 additions & 0 deletions standard/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,52 @@ Although an implicit conversion to `object` is permitted, a warning shall be iss
>
> *end example*

### §imp-collection-expression-conv Implicit collection expression conversions

An implicit collection expression conversion exists from a collection expression to the following types:

- A single-dimensional array type `T[]`, in which case, the element type is `T`.
- `System.Span<T>` and `System.ReadOnlySpan<T>`, in which cases, the element type is `T`.
- A type with an appropriate collection-creation method (§declaring-a-collection-type-general), in which case, the element type is the iteration type ([§13.9.5](statements.md#1395-the-foreach-statement)) determined from a `GetEnumerator` instance method or enumerable interface, not from an extension method.
- A struct or class type that implements `System.Collections.IEnumerable` where:

- The type has an applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)) constructor that can be invoked with no arguments, and the constructor is accessible at the location of the collection expression.
- If the collection expression has any elements, the type has an instance or extension method `Add` where:

- The method can be invoked with a single value argument.
- If the method is generic, the type arguments can be inferred from the collection and argument.
- The method is accessible at the location of the collection expression.

In which case, the element type is the iteration type of the type.
- Any of the following interface types:

- `System.Collections.Generic.IEnumerable<T>`
- `System.Collections.Generic.IReadOnlyCollection<T>`
- `System.Collections.Generic.IReadOnlyList<T>`
- `System.Collections.Generic.ICollection<T>`
- `System.Collections.Generic.IList<T>`

in which case, the element type is `T`

The implicit conversion exists if the type has an element type `U` where for each element `Eᵢ` in the collection expression:

- If `Eᵢ` is an *expression_element*, there is an implicit conversion from `Eᵢ` to `U`.
- If `Eᵢ` is a *spread_element* `..Sᵢ`, there is an implicit conversion from the *iteration type* of `Sᵢ` to `U`.

There is no collection expression conversion from a collection expression to a multi-dimensional array type.

Types for which there is an implicit collection expression conversion from a collection expression are the valid target types for that collection expression.

The following additional implicit conversions exist from a collection expression:

- To a nullable value type `T?` where there is a collection expression conversion from the collection expression to the value type `T`. The conversion is a collection expression conversion to `T` followed by an implicit nullable conversion from `T` to `T?`.

- To a reference type `T` where there is a collection-creation method associated with `T` that returns a type `U` and there is an implicit reference conversion from `U` to `T`. The conversion is a collection expression conversion to `U` followed by an implicit reference conversion from `U` to `T`.

- To an interface type `I` where there is a collection-creation method associated with `I` that returns a type `V` and there is an implicit boxing conversion from `V` to `I`. The conversion is a collection expression conversion to `V` followed by an implicit boxing conversion from `V` to `I`.

When a collection expression is converted to a ref struct type, all ref safety requirements ([§9.7.2](variables.md#972-ref-safe-contexts), [§16.5.15](structs.md#16412-safe-context-constraint)) shall be met.

## 10.3 Explicit conversions

### 10.3.1 General
Expand Down
Loading
Loading