Skip to content

Commit 5e93632

Browse files
BillWagnerNigel-Ecmajskeet
authored
Unconstrained type parameters and ?? (#1323)
* Unconstrainted type parameters and `??` Fixes #737 Add the language to the null-coalescing operator so that `??` can be used with an unconstrained type parameter. * Revert "Unconstrainted type parameters and `??`" This reverts commit a958a37. * Do this the easy way. I think this can be done the easy way. After reverting the original change, this commit removes the phrase "or a reference type" from the first bullet. By doing that, an unconstrained type parameter `T` is allowed. It's not a reference type. I tested the case Nigel and Kalle described for using `??` with an unconstrained type parameter, and the existing rules produce the behavior they described. I ran a few other cases to validate, and those worked correctly as well. * Update standard/expressions.md Co-authored-by: Nigel-Ecma <[email protected]> * add exclusion on unmanaged types Respond to Nigel's question * respond to feedback Change "a non-nullable value type" to "known to be a non-nullable value type". Add an example to illustrate that `a ?? b` is `a` when the type of `a` is a non-nullable value type used as a type argument to an unconstrained type parameter. * Update standard/expressions.md Co-authored-by: Jon Skeet <[email protected]> * extend the bulleted list. * fix test harness issue * Update standard/expressions.md * Update standard/expressions.md --------- Co-authored-by: Nigel-Ecma <[email protected]> Co-authored-by: Jon Skeet <[email protected]>
1 parent 04cae19 commit 5e93632

File tree

1 file changed

+15
-2
lines changed

1 file changed

+15
-2
lines changed

standard/expressions.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4822,14 +4822,27 @@ The null coalescing operator is right-associative, meaning that operations are g
48224822
48234823
The type of the expression `a ?? b` depends on which implicit conversions are available on the operands. In order of preference, the type of `a ?? b` is `A₀`, `A`, or `B`, where `A` is the type of `a` (provided that `a` has a type), `B` is the type of `b`(provided that `b` has a type), and `A₀` is the underlying type of `A` if `A` is a nullable value type, or `A` otherwise. Specifically, `a ?? b` is processed as follows:
48244824

4825-
- If `A` exists and is not a nullable value type or a reference type, a compile-time error occurs.
4825+
- If `A` exists and is an unmanaged type (§8.8) or known to be a non-nullable value type, a compile-time error occurs.
48264826
- Otherwise, if `A` exists and `b` is a dynamic expression, the result type is `dynamic`. At run-time, `a` is first evaluated. If `a` is not `null`, `a` is converted to `dynamic`, and this becomes the result. Otherwise, `b` is evaluated, and this becomes the result.
48274827
- Otherwise, if `A` exists and is a nullable value type and an implicit conversion exists from `b` to `A₀`, the result type is `A₀`. At run-time, `a` is first evaluated. If `a` is not `null`, `a` is unwrapped to type `A₀`, and this becomes the result. Otherwise, `b` is evaluated and converted to type `A₀`, and this becomes the result.
48284828
- Otherwise, if `A` exists and an implicit conversion exists from `b` to `A`, the result type is `A`. At run-time, `a` is first evaluated. If `a` is not `null`, `a` becomes the result. Otherwise, `b` is evaluated and converted to type `A`, and this becomes the result.
48294829
- Otherwise, if `A` exists and is a nullable value type, `b` has a type `B` and an implicit conversion exists from `A₀` to `B`, the result type is `B`. At run-time, `a` is first evaluated. If `a` is not `null`, `a` is unwrapped to type `A₀` and converted to type `B`, and this becomes the result. Otherwise, `b` is evaluated and becomes the result.
48304830
- Otherwise, if `b` has a type `B` and an implicit conversion exists from `a` to `B`, the result type is `B`. At run-time, `a` is first evaluated. If `a` is not `null`, `a` is converted to type `B`, and this becomes the result. Otherwise, `b` is evaluated and becomes the result.
4831+
- Otherwise, `a` and `b` are incompatible, and a compile-time error occurs.
48314832

4832-
Otherwise, `a` and `b` are incompatible, and a compile-time error occurs.
4833+
> *Example*:
4834+
>
4835+
> <!-- Example: {template:"standalone-console-without-using", name:"NullCoalescingGenerics"} -->
4836+
> ```csharp
4837+
> T M<T>(T a, T b) => a ?? b;
4838+
>
4839+
> string s = M(null, "text");
4840+
> int i = M(19, 23);
4841+
> ```
4842+
>
4843+
> The type parameter `T` for method `M` is unconstrained. Therefore, the type argument can be a reference type, or nullable value type, as shown in the first call to `M`. The type argument can also be a non-nullable value type, as shown in the second call to `M`. When the type argument is a non-nullable value type, the value of the expression `a ?? b` is `a`.
4844+
>
4845+
> *end example*
48334846
48344847
## 12.16 The throw expression operator
48354848

0 commit comments

Comments
 (0)