Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# nullable types and ternary operator #41883

Closed
vsfeedback opened this issue Feb 21, 2020 · 8 comments
Closed

C# nullable types and ternary operator #41883

vsfeedback opened this issue Feb 21, 2020 · 8 comments
Labels
Area-Compilers Area-Language Design Resolution-Duplicate The described behavior is tracked in another issue

Comments

@vsfeedback
Copy link

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.

@YairHalberstadt
Copy link
Contributor

This is a duplicate of dotnet/csharplang#2460

@Corey-M
Copy link

Corey-M commented Sep 30, 2020

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.

@YairHalberstadt
Copy link
Contributor

@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.

@ufcpp
Copy link
Contributor

ufcpp commented Sep 30, 2020

dup of dotnet/csharplang#33 in which #2460 is mentioned

@Corey-M
Copy link

Corey-M commented Sep 30, 2020

@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.

@YairHalberstadt
Copy link
Contributor

@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!

@Corey-M
Copy link

Corey-M commented Oct 5, 2020

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

@jcouv
Copy link
Member

jcouv commented Oct 6, 2020

Yes, this language question is tracked by dotnet/csharplang#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.

@jcouv jcouv closed this as completed Oct 6, 2020
@jcouv jcouv added Area-Language Design Resolution-Duplicate The described behavior is tracked in another issue labels Oct 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Area-Language Design Resolution-Duplicate The described behavior is tracked in another issue
Projects
None yet
Development

No branches or pull requests

6 participants