Target-Typed Conditional Expression
Note
This article is a feature specification. The specification serves as the design document for the feature. It includes proposed specification changes, along with information needed during the design and development of the feature. These articles are published until the proposed spec changes are finalized and incorporated in the current ECMA specification.
There may be some discrepancies between the feature specification and the completed implementation. Those differences are captured in the pertinent language design meeting (LDM) notes.
You can learn more about the process for adopting feature speclets into the C# language standard in the article on the specifications.
Conditional Expression Conversion
For a conditional expression c ? e1 : e2
, when
- there is no common type for
e1
ande2
, or - for which a common type exists but one of the expressions
e1
ore2
has no implicit conversion to that type
we define a new implicit conditional expression conversion that permits an implicit conversion from the conditional expression to any type T
for which there is a conversion-from-expression from e1
to T
and also from e2
to T
. It is an error if a conditional expression neither has a common type between e1
and e2
nor is subject to a conditional expression conversion.
Better Conversion from Expression
We change
Better conversion from expression
Given an implicit conversion
C1
that converts from an expressionE
to a typeT1
, and an implicit conversionC2
that converts from an expressionE
to a typeT2
,C1
is a better conversion thanC2
ifE
does not exactly matchT2
and at least one of the following holds:
to
Better conversion from expression
Given an implicit conversion
C1
that converts from an expressionE
to a typeT1
, and an implicit conversionC2
that converts from an expressionE
to a typeT2
,C1
is a better conversion thanC2
ifE
does not exactly matchT2
and at least one of the following holds:
Cast Expression
The current C# language specification says
A cast_expression of the form
(T)E
, whereT
is a type andE
is a unary_expression, performs an explicit conversion (§10.3) of the value ofE
to typeT
.
In the presence of the conditional expression conversion there may be more than one possible conversion from E
to T
. With the addition of conditional expression conversion, we prefer any other conversion to a conditional expression conversion, and use the conditional expression conversion only as a last resort.
Design Notes
The reason for the change to Better conversion from expression is to handle a case such as this:
M(b ? 1 : 2);
void M(short);
void M(long);
This approach does have two small downsides. First, it is not quite the same as the switch expression:
M(b ? 1 : 2); // calls M(long)
M(b switch { true => 1, false => 2 }); // calls M(short)
This is still a breaking change, but its scope is less likely to affect real programs:
M(b ? 1 : 2, 1); // calls M(long, long) without this feature; ambiguous with this feature.
M(short, short);
M(long, long);
This becomes ambiguous because the conversion to long
is better for the first argument (because it does not use the conditional expression conversion), but the conversion to short
is better for the second argument (because short
is a better conversion target than long
). This breaking change seems less serious because it does not silently change the behavior of an existing program.
The reason for the notes on the cast expression is to handle a case such as this:
_ = (short)(b ? 1 : 2);
This program currently uses the explicit conversion from int
to short
, and we want to preserve the current language meaning of this program. The change would be unobservable at runtime, but with the following program the change would be observable:
_ = (A)(b ? c : d);
where c
is of type C
, d
is of type D
, and there is an implicit user-defined conversion from C
to D
, and an implicit user-defined conversion from D
to A
, and an implicit user-defined conversion from C
to A
. If this code is compiled before C# 9.0, when b
is true we convert from c
to D
then to A
. If we use the conditional expression conversion, then when b
is true we convert from c
to A
directly, which executes a different sequence of user code. Therefore we treat the conditional expression conversion as a last resort in a cast, to preserve existing behavior.
C# feature specifications