Roslyn: C# nullable types and ternary operator

Created on 21 Feb 2020  Â·  8Comments  Â·  Source: dotnet/roslyn

_This issue has been moved from a ticket on Developer Community._


Hello.
I'll show on example:

// Example 
DateTime? temp1 = null; // OK
DateTime? temp2 = String.IsNullOrEmpty("some string") ? temp1 : DateTime.Now; // OK
DateTime? temp3 = String.IsNullOrEmpty("some string") ? null : DateTime.Now; // Error: CS0173 C# Type of conditional expression cannot be determined because there is no implicit conversion between '<null>' and 'DateTime'
DateTime? temp4 = String.IsNullOrEmpty("some string") ? (DateTime?) null : DateTime.Now; // OK, but this is not very obvious

On the left, I explicitly set type is "DateTime?", maybe the compiler could define what is expected from the ternary operator, this would be more obvious and convenient.
Thanks.


Original Comments

Visual Studio Feedback System on 2/14/2020, 03:06 AM:

Thank you for taking the time to provide your suggestion. We will do some preliminary checks to make sure we can proceed further.  We'll provide an update once the issue has been triaged by the product team.

Area-Compilers Area-Language Design Resolution-Duplicate

Most helpful comment

@Corey-M

I understand your opinion, but this isn't the right forum for them - CSharplang is where language design discussion goes.

It is often the case that the language design team makes decisions that other people don't like. It happens to me all the time. In fact it often happens to members of the team, since they of course often disagree with each other. In such cases it's sometimes possible to change their mind, but you have to do it in a specific way:

You have to show them something they didn't know. They already know all the theoretical arguments for one over the other and rejected it. Instead of repeating them, you have to bring data showing that a large percentage of use cases would not be fixed by their solution.

For example you could scan dotnet/runtime, find all cases where they have to cast one side of a ternary to get it to compile, and count how many their solution would fix, how many yours would fix, and how many both would fix.

I'm other cases you can't change their mind, and that's life. Better luck next time!

All 8 comments

This is not a duplicate of dotnet/csharplang#2460 since that only considers the target type. This should in fact be a separate issue regarding type determination in the ternary operator.

The C# language specification (which is horribly out of date) says:

The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,
• If X and Y are the same type, then this is the type of the conditional expression.
• Otherwise, if an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
• Otherwise, if an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
• Otherwise, no expression type can be determined, and a compile-time error occurs.

When one of the result expressions is the null keyword and the other has a reference or nullable type then there is no problem. The issue arises when one expression has a non-nullable value type and the other is the untyped null. In this case the simple solution is to change the result type of the ternary operator to the nullable form of the value type.

dotnet/csharplang#2460 proposes the target type be used. This is fine when that type is known, but not possible when the type is unknown. For instance:

var source = "non-numeric string";
var value = int.TryParse(source, out var v) ? v : null;

Currently that is not allowed. Instead we have to explicitly type the null to (int?) to get the thing to compile. But there's really no good reason to need to do so since it is clear that the result will be int? anyway, if only the compiler could take the single step to figure that out.

The proposal would then amount to adding the following to the type resolution rules prior to the failure condition:

  • If one of X or Y is an untyped null expression and the other is a non-Nullable<> value type T, where T is a valid generic type argument for Nullable<> then the type of the conditional expression is Nullable<T>.

That seems reasonably straightforward.

In the case of #nullable or equivalent this - or a similar rule - would apply to non-nullable reference types.

@Corey-M

This issue is about target typing the ternary operator:

On the left, I explicitly set type is "DateTime?"

I would suggest that you open a new issue (on dotnet/CSharplang) about your suggestion.

@YairHalberstadt For nullable value types specifically target typing is a solution, but it is unnecessary for this issue. A simple addition to the type determination rules for ternary operators handles this case and any other where a value type and an untyped null are used as the potential results. And it doesn't help in the case of var x = true ? 1 : 0; or similar since there is no target type.

Yes, the poster mentions that he supplied a target type, but the simplest resolution here also fixes this in the very common (according to questions on StackOverflow anyway) problem of untyped nulls causing compiler errors.

This is an intuitive fix that covers things that target typing explicitly cannot handle and still leaves all of the cases that only target typing can handle alone. It solves the problem of this issue perfectly and simply, and implementation is honestly trivial.

Oh, and that trivial, useful fix just got rejected by the design team in favor of a "solution" that doesn't fix most of these problems at all.

@Corey-M

I understand your opinion, but this isn't the right forum for them - CSharplang is where language design discussion goes.

It is often the case that the language design team makes decisions that other people don't like. It happens to me all the time. In fact it often happens to members of the team, since they of course often disagree with each other. In such cases it's sometimes possible to change their mind, but you have to do it in a specific way:

You have to show them something they didn't know. They already know all the theoretical arguments for one over the other and rejected it. Instead of repeating them, you have to bring data showing that a large percentage of use cases would not be fixed by their solution.

For example you could scan dotnet/runtime, find all cases where they have to cast one side of a ternary to get it to compile, and count how many their solution would fix, how many yours would fix, and how many both would fix.

I'm other cases you can't change their mind, and that's life. Better luck next time!

The relevant csharplang issue has, after some discussion, resulted in the feature being sent back to triage for possible inclusion in C# 10.

Yes, this language question is tracked by https://github.com/dotnet/csharplang/issues/33 already.
If I recall, the addition of target-typing for ternaries prevents us from improving the best common type algorithm in the future (or makes it very complex), so most likely this proposal won't be implemented. One can use a cast or a target-type at the moment.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

glennblock picture glennblock  Â·  3Comments

NikChao picture NikChao  Â·  3Comments

AdamSpeight2008 picture AdamSpeight2008  Â·  3Comments

binki picture binki  Â·  3Comments

asvishnyakov picture asvishnyakov  Â·  3Comments