-
Notifications
You must be signed in to change notification settings - Fork 1k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Discussion: null
evaluation operator to boolean
#196
Comments
I believe that the decision that C# could only evaluate Boolean conditions (and limits what can be considered such) was very intentional, specifically to avoid the assortment of logic errors caused by minor typos in the C language. For example, your change would make the following legal, and very wrong: if (obj = null) { // oops!
DoIfNotNull();
} |
@HaloFour I agree that the code you showed should not be allowed, but this proposal does not suggest to allow it. Instead, you would have to include the if (?(obj = null)) Or: if ?(obj = null) I think this means the error is not easy to make this way, which is why I personally don't oppose this proposal. (I'm curious why so many people downvoted this. Did they misunderstood the proposal too? Or do they just not like the syntax or the proposal in general?) |
Didn't downvote but I'm far from convinced that adding syntax makes null checking any more readable than |
@HaloFour It is good that it was made intentionally, actually I think that it is a side product of type safety. I do not intend to allow String s = null;
if ( s?.Length > 0) { ... } where first @jnm2 I want to avoid writing things like while ( ( item = collection?.Next() ) != null ) { ... } when they can be written while ( ?! item = collection?.Next() ) { ... } I think it would be enough to make this available to all if-condition like contexts, where the condition evaluates to true like in C where it is not So it actually comes down to an operator that implicitly cast every type to |
Such an operator could be while ?!( item = collection?.Next() ) { ... } |
As far as I understand the proposal of @lachbaer, he did not suggest that C# adopts the automatic conversion of everything to bool. Instead he suggests a unary prefix operator '?' for all reference type, that checks if the reference is not static public bool operator<T> ? (this T reference) where T : class
{
return reference != null;
}
Instead of the automatic conversion to bool from C/C++, C# would require explicit usage of the suggested new 'notnull' operator to 'convert' the reference to For example .. if (?obj) DoSomethingWith (obj); .. would be a shorter version of .. if (obj != null) DoSomethingWith (obj); That shorter version is at least as readable as the null-conditional operator '?.' which was introduced in C# 6: obj?.DoSomething (); Because if (!(?obj)) obj = new FooBar (); .. or even better (if precedence and associativity of the new operator are defined apropriately) without additional parenthesis .. if (!?obj) obj = new FooBar (); .. as a shorter version of .. if (obj == null) obj = new FooBar (); The motivation is of course to have a syntax for reference checking, that is almost as short as in C/C++, but without inheriting all the problems and bugs of automatic conversion to bool. In my humble opinion, the suggested notnull operator would be really nice to have in a future version of C#. Update: |
Hmm, looks like there are several questions:
|
If you like to write so many question marks and exclamation marks I just want to let you know that there is this beautiful language Ook?!. |
Meanwhile I would go with static public bool operator<T> ? (this T value)
=> value != default(T); because there are ongoing discussions (sorry, don't have the threads by hand) about defining own if (obj == null) obj = new FooBar ();
// vs.
if (!?obj) obj = new FooBar (); I think obj = obj ?? new FooBar();
// or with roslyn/dotnet#205
obj ??= new FooBar(); |
I find the first example much more readable and natural, so there is definitely subjectivity to this. Also,
If you remove the parentheses, I'd expect operator precedence to be the equivalent of Since the parentheses are necessary, the question is whether |
I don't feel as But here the negation is more of a subject: Would if ( !! obj ) { UseObject(); }
|
You're right, I jumped the gun on responding there. That said, this proposal seems to be about adding a "Truthiness" operator to C#. What real benefit does that provide? Is actually explicitly specifying the comparison being performed such a burden? I don't think so. Even assuming C# doesn't totally botch up "Truthiness" to the extent that JavaScript did I don't see any need to have some manner through which arbitrary values are evaluated to a Boolean result. It hides too many details, such as the specific type and what that comparison really means. You save typing a few characters at the expense of understanding what that code will do later. Not worth it, in my opinion. What would the following mean? Null check? Zero check? Both? int? x = 0;
if (?x) { ... } |
@HaloFour
int? x = 0;
if (!!x) { ... } it would mean "if x is set to something other than it's default value". For ref types that would be int? x = 0;
if ( x.HasValue && x != 0 ) { ... } // or just `x != 0` |
Fairly sure that will result in ambiguities. Either way, the syntax of the operator is less important than what it does. It hides how the comparisons are made, and in the case of nullable structs there aren't "right answers". |
For me PS: The character is currently not of interest, let it be |
So you expect the following to print bool? b = false;
if (§b) {
Console.WriteLine("yay?");
} |
@HaloFour
So in this case it would be equal to bool? b = false;
if (b.GetValueOrDefault() != default(bool)) { Console.WriteLine("yay?"); } |
After thinking about the suggestion for a couple of hours, let me bore you with my latest brain dump ..
|
I tried to write some static methods to simulate an operator public static bool asbool1<T>(T value) where T : class
=> value != null;
public static bool asbool2<T>(T value) where T : struct
=> ! value.Equals( default(T) );
public static bool asbool3<T>(T? value) where T : struct
=> ! value.GetValueOrDefault().Equals(default(T)); It is not possible to give all 3 methods the same name. That, too, encourages me to vote for an appropriate operator. I really still like the idea of having the explicit possibility of C-like boolean evaluations, |
I think null coalescing assignments satisfies a big part of the use case motivating this request. |
@MgSam : No, 'null coalescing assignments' covers only the special case, where the checked reference is assigned after the check: r ??= new Foo ();
if (r == null) r = new Foo (); This suggestion is about any reference check, independent of the code after the check. |
Two questions still arise to me: 1. How to deal with Nullable Types?The operator should work on the 2. Is
|
May be a single
|
The key to your static methods is to treat the struct version as a special case, as it can't be public static bool IsNullOrDefault<T>(T value) where T : class =>
value != null;
public static bool IsNullOrDefault<T>(T? value) where T : struct =>
!value.GetValueOrDefault().Equals(default(T));
public static bool IsDefault<T>(T value) where T : struct =>
!value.Equals(default(T)); |
@lachbaer If |
@DavidArno 👍 And now as an operator, please. 😁 @yaakov-h Yes, that came to my mind yesterday night as well. An unary
This doesn't look so intuitive to me. |
Probably the most confusing thing is that your proposal seems to be trying to bring an aspect of C into C#, but completely inverts the boolean result of the expression. The 2nd and 3rd columns of your table are complete opposites. |
That's what I meant with "This doesn't look so intuitive to me" |
Above, @lachbaer asked:
|
In my previous post I mentioned precedence and associativity of the suggested check operator several times, but did not specify them.
if (?(x?.y)) ... // same meaning as below, but easier to understand
if (?x?.y) ... // same meaning as above See lines [4c] and [4d] in my previous post. |
@MillKaDe Thank you very much for your comprehensive exposition! 😄 👍 First I wasn't sure whether On the second thought it might be better, more C# alike, not to mix up if ( ?( (t?).Value ) ) ...;
// eqals:
if (? t.GetValueOrDefault() ) ...;
For Nullables's this view might be the best, because one will most probably use them, when only So essentially |
I think the follwing two lines from two posts above need some clarification: if (?t && ?t.Value) ...; // [3d]
// A B
if (?t?.Value) ...; // [4d]
// B A In line [3d] the first part In cases where the null check is followed by a member access, we can already use the existing null-conditional member access That means the first '?' (new null check op) in The second '?' (new null check op) in By the way, that is another hint, that '?' might the best pick for the operator character. |
Please describe some one who already dived into this problem deeply, what is wrong with this approach:
? |
to @lachbaer I agree that |
@MillKaDe Yes @dmitriyse Sorry, I don't understand you. What does |
Mathematical Identity it's usually defined by symbol ≡. Just read null ≡ false or "interpreted as false". if (someRefVar) // allowed
{
}
if (!someRefVar) //not allowed use classic if (someRefVar == null)
{
}
if (someRefA && someRefB) // allowed
{
}
if (someRefA & someRefB) // not allowed
{
} Advantage - no any new operators, intuitive. |
to @dmitriyse I fear I don't understand your question(s).
|
to @dmitriyse Ah, OK - now I understand a bit more .. |
Oh, and there is an aspect we have not discussed yet - should it be possible to overload that new check operator ? But what about user defined value types, e.g. Point p; // a user defined struct / value type
if (?p) .. // what does that mean ? Should that be legal ? Does it mean |
@MillKaDe Thanks for you description. I forgot about "if (a=b)" like problems. |
@MillKaDe @dmitriyse This proposal is about a "is-non-default" operator. Maybe one day there will be an The correct equivalent of bool isNotNullOrDefaultQuestionmarkOperator<T>(T x) where T : class
=> ! ( ( x == null ) || ( x.Equals(default(T)) ) );
bool isNotNullOrDefaultQuestionmarkOperator<T>(T x) where T : struct
=> x.Equals(default(T));
bool isNotNullOrDefaultQuestionmarkOperator<T>(T? x) where T : struct
=> x.HasValue; |
No, not in my understanding (see directly above). The 'overloading' would be done with a |
About non-zero defaults: Why ? see this answer by Jon Skeet on stackoverflow. A class / reference type In C# a struct / value type struct S {
public readonly MyDefaultValueForS = new S (42);
} but When an array of S No matter how complex a type definition is, it always forms a tree-like graph. All fields of any type are always either
but any embedded struct follows these rules too, so sooner or later you will reach the leafes of the tree/graph and all leafs are either
Introducing some way to define non-zero default values - no matter how - would be a breaking change, which the language design team tries to avoid by all means. About overloading the new check operator: The new check operator should be predefined and not overloadable for:
The check operator would be most useful for references, and also useful for ints. I don't think it makes sense to allow the user to define the check operator for user defined structs, because its implementation would always have to be the very same: public static bool operator ? (S s) {
foreach (var field in struct.fields) {
if (?field.value) return true; // found one field with non-default value
}
return false; // all fields have default value
} However, this implementation could be a generated by the compiler. As far as I know, the method |
In Roslyn I (also) found Debug.Assert(!isExtensionMethod || (receiverOpt != null)); Because Debug.Assert((isExtensionMethod == false) || (receiverOpt != null)); throughout the codebase. Or we have another argument in favor of this proposal, because Debug.Assert(!isExtensionMethod || ?receiverOpt); I like brevity - as long as it is understandable. So I still like the |
Meanwhile I do not think that there should be an implicit operator like this. For value types this could be achieved in a safe way by delacring custom 'struct' types, when really needed. public struct BoolInt
{
public int Value { get; set; }
public BoolInt(int value) => Value = value;
public static implicit operator bool(BoolInt rhs) => rhs.Value != 0;
public static implicit operator BoolInt(bool rhs) => rhs ? -1 : 0;
public static implicit operator int(BoolInt rhs) => rhs.Value;
public static implicit operator BoolInt(int rhs) => new BoolInt(rhs);
/* further operators */
}
BoolInt v1 = 10;
while (v1) --v1; I'd rather see an if (?secondItem) {
var item = fistItem ?? secondItem;
item?.Execute();
} In all 3 scenarios the |
See also #545 for specific proposal of |
Why not just have the editor change if (obj) to if (obj != null) as autocorrect or AI assisted feature. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Since the beginning of C# I dislike the extensive use of
obj == null
comparisons.On one hand C# is quite verbose and needs a handful of charaters to express a simple thing, on the other hand there is done quite effort to shorten code, like e.g. expression bodies, null-coalescing operators, etc.
From C I much like the fact that
null
evaluates to booleanfalse
and!null
to true.I'm not sure about a possible syntax. Because C# is type safe and the existing
obj == null
has its reason, an operator must be something conspicuous to make clear that the following reference is evaluated totrue
orfalse
. It could be e.g. an unary?
operator (like e.g. unary+
,-
and~
)Together with #157, where putting the not operator
!
in front offor
andwhile
clauses to negate the expression it could be something likeThe text was updated successfully, but these errors were encountered: