Description
- Proposal added (below)Discussed in LDM 2019-04-22Decision in LDM (Approved for C# 8)Finalized (done, rejected, inactive)Spec'ed
@333fred asked me
Given the following code, we cannot determine a best type for the switch expression and the code does not compile. Why can't we determine that the best type is
A
, given that's the target type of the expression?class A { } class B : A { } class C : A { } class D { void M() { A a = null; a = a switch { B b => b, C c => c, _ => throw new System.Exception(), }; } }Casting one branch to
A
fixes the code
My initial reaction was
We use the same inference algorithm for
new []{ b, c }
and() => { if (b) return b; else return c; }
andc ? b : c
andM(b, c)
whereT M<T>(T x, T y)
. The general philosophy is that we avoid inferring a type that wasn't one of the input types. What if they had a common interface but not a common base class? What if they had two common interfaces? We just don't do that. We use "target type" only for certain conversions. We don't have a "conversion from array creation" or "conversion from conditional expression" and similarly we won't have a "conversion from switch expression".
However, my answer was not satisfactory to either @333fred or myself.
This is a proposal to "do that".
Proposal
Currently, a switch expression is specified to have a type that is the inferred common type of the expressions in the switch arms. It is (currently) required that there be such a common type.
Instead, I propose
- We attempt to infer a common type among the arms of the switch expression. If there is such a common type, that is the (natural) type of the switch expression.
- There is a switch expression conversion (a new implicit conversion-from-expression) from a switch expression to the type
T
if there is an implicit conversion from every arm's value expression toT
. - Let
T
be the type of the switch expression, either the target type of the switch expression conversion, or if it was not subject to such a conversion the expression's natural type. It is an error ifT
does not exist. It is an error if a switch arm's expression cannot be implicitly converted toT
.
Activity
CyrusNajmabadi commentedon Apr 4, 2019
I really like this. But i definitely beg of you to do the same for
x ? y : z
at the same time if you do this :)YairHalberstadt commentedon Apr 4, 2019
Is there any reason not to do this generally, for all expressions?
Thaina commentedon Apr 4, 2019
If I remember correctly this feature was already requested for
x ? y : z
along with return type of linq and should work with struct/nullable but I don't remember the issue about itJoe4evr commentedon Apr 4, 2019
@Thaina #33.
DavidArno commentedon Apr 4, 2019
I like this idea, as long as it works with interfaces, not just inherited types:
spydacarnage commentedon Apr 4, 2019
DavidArno commentedon Apr 4, 2019
An interesting point related to this was raised in #2387. To clarify, @gafter, when you talk of "target typed", are you referring to the type of the target of the expression? For example,
would this mean that if that switch were a "target typed switch", then the type of
x
,int?
, would be used? Or would the type be inferred from the arms of the switch? In other words, would the following work:where the type of the switch is inferred from a common type shared by
B
,C
andnull
and thus the type ofa2
is then inferred from the switch? Or wouldvar
not work here as the switch needs to know the type ofa2
in advance?alrz commentedon Apr 4, 2019
Also useful in null-coalescing operator (from #877)
Casting either of operands in any of these should be unnecessary.
CyrusNajmabadi commentedon Apr 4, 2019
@alrz good point. I think the general idea of like to see investigated is that if we're in a position where there is some Target-type, and if you're using one of the 'branchy' expressions (i.e. switch-exoression,
?:
,??
), and if that expression doesn't produce a common type, then Target-type the branches.Both
?:
and??
can be thought of as abbreviations of the generalized switch-expression, so it makes sense to me to apply this to them. This would also hold for me in the future for any other construct like thisgafter commentedon Apr 5, 2019
@DavidArno That example would not work. Either there has to be a common type (as currently specified), or there has to be a target type. That example has neither.
@spydacarnage Yes, this would work in a return statement, on the right-hand-side of an assignment statement, as an argument to a method call (even if there are different overloads with different parameter types), and even for a switch expression that is inside another switch expression. There are lots of places where there is a target type.
spydacarnage commentedon Apr 5, 2019
@gafter That's excellent. I can't wait :-)
31 remaining items