Closed lachbaer closed 7 years ago
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 ?
operator:
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 == null
already is.
@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 obj = null
, at least not if explicitly stated to be allowed. It should be something like
String s = null;
if ( s?.Length > 0) { ... }
where first s?.Length
is converted to nullable type int?
and then compared to (int?) 0
, what produces false in case of s being null
. But here it is just for obj == null
, like in C and type safe.
@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 null
, 0
, 0.0
or false
, respectively default(T)
.
So it actually comes down to an operator that implicitly cast every type to (bool)
. And that operator should be brief.
Such an operator could be ?!
, because ?
is already used in many nullable scenarios and !
just stating not, so ?! => null not [not null]. It should have the same precedence as assignment operators, evaluated from right to left.
And together with #157 it could be written before the condition braces
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 null
, e.g. something like this:
static public bool operator<T> ? (this T reference) where T : class
{
return reference != null;
}
Yes, thats not valid C#, but I hope everybody understands what it means anyway ...
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 bool
.
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 bool
already has an unary prefix operator ! to negate a bool
, checking for null
(instead of not null
) would be possible by combiniation of '!' and '?', e.g. writing ..
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:
I would prefer if (?obj) ..
over if ?(obj) ..
.
Hmm, looks like there are several questions:
1) do we want/need an operator (e.g. '?') to check for (x != null)
?
2) whats the best syntax ?
3) do we want/need another operator (e.g. '!?') to check for (x == null)
?
4) can we use just use the first operator (?) combined with bool negation (!) instead of a second operator ?
5) shall the operator be defined only for references (x != null)
or extended to value types (x != 0)
or (x != default(X))
?
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 =! default(T)
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 default
's for value types, defaults for Tuples and so on.
if (obj == null) obj = new FooBar ();
// vs.
if (!?obj) obj = new FooBar ();
I think !?obj
is a bit cryptic and confusing in this way and shouldn't be used (maybe issue a warning), but it would be legal. It would be better to write that example with
obj = obj ?? new FooBar();
// or with roslyn/dotnet#205
obj ??= new FooBar();
@lachbaer
@jnm2 I want to avoid writing things like
while ( ( item = collection?.Next() ) != null ) { ... }
when they can be written
while ( ?! item = collection?.Next() ) { ... }
I find the first example much more readable and natural, so there is definitely subjectivity to this.
Also, !item = x
is currently the same as saying (!item) = x
which is illegal (CS0131), so I would expect that you'd need the parentheses:
while ( ?!(item = collection?.Next()) ) { ... }
If you remove the parentheses, I'd expect operator precedence to be the equivalent of (?!item) = collection?.Next()
which should fail with CS0131.
Since the parentheses are necessary, the question is whether ?(expression)
is more readable than (expression) == null
. I'm still far from convinced.
I don't feel as ?
is the right character for this, because by now the ?
operator says "if it is set, then use it", like with ?.
and ??
.
But here the negation is more of a subject: if (obj != null) ThenUseIt();
Would !!
stand out better?
if ( !! obj ) { UseObject(); }
!
is a unary boolean already and ! !
would override itself, so to say being neutral. I doubt anybody wrote this ( or bool b = !!!!!!!!!! true;
๐ ) in actual code, so two !!
would take precedence during parsing.
@svick
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
As stated above I don't think that ?
is the right operator for this. I'll go with !!
for now.
What would the following mean? Null check? Zero check? Both?
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 x!=null
, for nullable type it would also be null, so the above statement would be true.
But you are right that one could understand it as if (x!=0)
. So maybe in the case of standard value types it should just be both, giving the meaning "if x has something valuable to work with".
int? x = 0;
if ( x.HasValue && x != 0 ) { ... } // or just `x != 0`
@lachbaer
As stated above I don't think that
?
is the right operator for this. I'll go with!!
for now.
Fairly sure that will result in ambiguities. !
is already the unary negation operator and the compiler allows you to apply it multiple times if you see fit. bool b = false; if(!!b) { }
is already perfectly legal.
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".
@HaloFour
and in the case of nullable structs there aren't "right answers"
For me =! default(T)
is quite definite, and if it is extended for nullable primitive types to =! 0
(or alike) I think that's also clear. No, I don't see any ambiguities.
PS: The character is currently not of interest, let it be ยง
if you like ๐ I just think that ?
is definitely not appropriate
@lachbaer
So you expect the following to print yay
? I certainly wouldn't. I highly doubt that the majority of developers would. It's very unintuitive.
bool? b = false;
if (ยงb) {
Console.WriteLine("yay?");
}
@HaloFour
No it wouldnt print
yay`. I will quote myself:
it is extended for nullable primitive types to =! 0 (or alike)
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 ..
1) The main question is:
Do we need or want an alternative way to check if some variable or expression
x
of typeX
has or has not default value ofX
(default(X)
=null
orfalse
or0
or0.0
or whatever).
2) The motivation is to have some shorter syntax, which does not contain the default value
null
, 0
, ...or implicitely: default (X)
because otherwise we could just close the case and continue to compare x
to the explicit or implicit default value, e.g.
x != null
x == 0
x != default(X)
3) No matter what the syntax will be, the result of the check is always either true
or false
, so the result type is always a simple two-valued bool
(and not bool?
or anything else with more than two states/values).
4) Some possible types X
of the variable or expression x
to be checked for default value are:
bool
float
, double
and maybe decimal
[u]int
, [u]long
, [u]short
or [s]byte
x
is a referencedefault(X)
is a bunch of zero bits (remember that for initialization of value types no default parameterless constructor is called, instead the CLR just zeroes the memory)5) Comparing ..
x
to true
or false
explicitely is just for bloody newbies - x
is already bool
0.0
is not terribly smart either, cause x
might be very close to zero but still not exactly equalx
to 0
might make sense, e.g. SomeCollection.Count != 0
or SomeArray.Length != 0
x
: in most cases, enums should only be compared to their named valuesa reference x
: either we compare it to null
or to some other reference y
(which might be null or not null).
Looks like the suggested new check operator only makes sense for references and ints - and maybe for other struct/value types.
6) It is already possible to define two extension functions for checking references:
public static class Ext0 {
public static bool Not0<C> (this C c) where C : class => c != null;
public static bool Is0<C> (this C c) where C : class => c == null;
}
void Usage (C c) {
// some checks for not-null
if (c != null) FooBar (); // classic
if (c.Not0 ()) FooBar (); // not that much better
// some checks for null
if (c == null) FooBar (); // classic
if (c.Is0 ()) FooBar (); // not that much better either
}
If C# had extension properties, we could omit the ()
, but it would still look ugly.
7) The check of x
could be used:
if (x != null) ..
while (x != null) ..
for (X x = list.head; x != null; x = x.next) ..
var z = x != null ? a : b;
var z = x != null;
as a function argument var z = func (x != null)
.
Note that sometimes the check is surrounded by parenthesis ()
and sometimes not.
That means pulling the check operator out of those parenthesis
if ?(x)
instead ofif (?x)
is sometimes not possible, because sometimes there are no parenthesis.
8) Conclusion:
'?.'
in reference?.member
. That points to using question mark '?' as check operator.'!'
cant be the check operator.?x
means x != null
!?x
means !(?x)
means !(x != null)
means x == null
!?x
is not one new operator '!?', but the combination of the suggested new check operator '?' and the existing negation operator '!'.9) Some syntax examples for comparison:
C# | C# with ? | C/C++ |
---|---|---|
if (r != null) | if (?r) | if (r) |
if (r == null) | if (!?r) | if (!r) |
while (r != null) | while (?r) | while (r) |
for (var n = list.head; n != null; n = n.next) | for (var n = list.head; ?n; n = n.next) | for (var n = list.head; n; n = n.next) |
x != null ? a : b | ?x ? a : b | x ? a : b |
x == null ? a : b | !?x ? a : b | !x ? a : b |
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. The idea is to adopt an existing and frequently used idiom from C/C++, but without inheriting the well known problems.
Two questions still arise to me:
The operator should work on the default(T)
. For nullable-value-types that would be null
also.
But wouldn't it be better to have it working on Nullable<T>.GetValueOrDefault()
?
?
the right operator?
is actually used for null
-implying conditions. But here we are talking also about figures like 0, 0.0, etc.
Especially for the nullably-value-types, ?
would more stand for null
-only and not for GetValueOrDefault()
.
May be a single @
sign would stand out more. It can be read as a "h@s" sign, claiming that the following expression has a powerful value other than 0 or null or false, thus emulating the C/C++ meaning of "truth".
@
targets the def@ult values and in contrast to ?
it wouldn't emphasize the null
meaning. Also some expressions look more meaningful (e.g. "!@") and emphasize the special character of the condition, see below.
C# | C# with @ | C/C++ |
---|---|---|
if (r != null) | if (@ r ) | if (r) |
if (r == null) | if (!@ r) | if (!r) |
while (r != null) | while (@ r ) | while (r) |
for (var n = list.head; n != null; n = n.next) | for (var n = list.head; @n; n = n.next) | for (var n = list.head; n; n = n.next) |
x != null ? a : b | @x ? a : b | x ? a : b |
x == null ? a : b | ! @x ? a : b | !x ? a : b |
The key to your static methods is to treat the struct version as a special case, as it can't be null
and so give it a different name. Then all works as you want:
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 @x
were to mean x != null
or x != default(T)
, then what about @"hello"
? That would be ambiguous between a string, or an always-true expression.
@DavidArno :thumbsup: And now as an operator, please. ๐
@yaakov-h Yes, that came to my mind yesterday night as well.
An unary %
-operator would be available, o/o
could mean "boolean", or "bunch of 0
s". But this would rather mean to negate the meaning of "not null (0)" to "is null (0)". Thus the complete operator would be "!%":
C# | C# with % (!%) | C/C++ |
---|---|---|
if (r != null) | if (!% r ) | if (r) |
if (r == null) | if (% r) | if (!r) |
while (r != null) | while (!% r ) | while (r) |
for (var n = list.head; n != null; n = n.next) | for (var n = list.head; !%n; n = n.next) | for (var n = list.head; n; n = n.next) |
x != null ? a : b | !%x ? a : b | x ? a : b |
x == null ? a : b | %x ? a : b | !x ? a : b |
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.
@yaakov-h
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:
How to deal with Nullable Types?
1) A declared, but not explicitely initialized Nullable<T>
is filled with zero bits. A bunch of zero bits of type Nullable<T> t
means
t.HasValue
is false
t.Value
is default (T)
2) So what would / should be the meaning of the check operator when applied to a Nullable ?
Conceptually, Nullable<T>
is a wrapper around a (non-nullable) value type T
, that (by wrapping) adds an additional value null
.
If it is a wrapper, I think it makes sense to progress from outside to inside.
So first we have to check for null
to know if it has a value at all, and then we can check the value for 0
or default (T)
.
3) First, lets just check if t
has a value:
Nullable<T> t; // or shorter: `T? t`
if (t.HasValue) ...; // [1a] old
if (t != null) ...; // [2a] old
if (?t) ..; // [3a] new
Next we check if t
has a value, and if that value is different from the default value:
if (t.HasValue && t.Value != default (T)) ...; // [1b] old
if (t != null && t.Value != default (T)) ...; // [2b] old
if (?t && t.Value != default (T)) ...; // [3b] new
4) With the new check operator, t.Value != default (T)
could be shortened to ?(t.Value)
:
if (t.HasValue && ?(t.Value)) ...; // [1c]
if (t != null && ?(t.Value)) ...; // [2c]
if (?t && ?(t.Value)) ...; // [3c]
And maybe the third line [3c] could be shortened even more to:
if (?(t?.Value)) ...; // [4c] new
If precendence and associativity are defined wisely, if might be even possible omit the parenthesis:
if (t.HasValue && ?t.Value) ...; // [1d]
if (t != null && ?t.Value) ...; // [2d]
if (?t && ?t.Value) ...; // [3d]
if (?t?.Value) ...; // [4d]
5) That fourth line [4d] looks a bit weird, so lets write it with explicit parenthesis again to show the evaluation sequence:
if (?((t?).Value)) ...; // [4e]
This first '?' is the new check operator, but the second '?' is the already existing null-conditional operator applied to nullable t
.
That null-conditional operator (second '?') is evaluated first:
t
is null / has no value, t?
resolves to null
, and therefore t?.Value
resolves to null
.if t
is not null / has a value, t?
resolves to t
, and therefore t?.Value
resolves to t.Value
.
In the first case (t is null
) we now have:
if (?(null)) ...;
which resolves to false
. Is that correct ?
Does
t
have a value and is that value different from the default value ?
No, becauset
has no value.
Looks like this result is correct.
In the second case (t is not null
) we now have:
if (?(t.Value)) ...;
which is equivalent to t.Value != default (T)
.
Looks like that result is correct, too.
6) I admit, that ?t?.Value
might look confusing.
But whenever different operators mix in C# / C++ / C / whatever,
(...)
until the meaning is crystal clear.In my previous post I mentioned precedence and associativity of the suggested check operator several times, but did not specify them.
1) Associativity is only relevant for binary operators ...
2) Precedence
The suggested check operator should have the same precedence as the other unary operators (e.g. +x
, -x
, !x
, ~x
, ...). Since member access x.y
and null-conditional member access x?.y
are primary operators, they have higher precedence. This allows to omit the inner parenthesis ()
in
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 ?t?.Value
for Nullable's is the right way, because my intention was to have an operator that 'boolifies' variables directly, like C/C++, without any further sub-expressions (.Value
) required.
On the second thought it might be better, more C# alike, not to mix up null
and 0
. Besides ?x?.Value
can already be (syntactically) abbrevated and expressed more clearly:
if ( ?( (t?).Value ) ) ...;
// eqals:
if (? t.GetValueOrDefault() ) ...;
t.GetValueOrDefault()
already returns the default(T)
value of T?
if t is null
, therefore the check will only be true
if t has a value different than all bits 0
, default(T)
respecively.
For Nullables's this view might be the best, because one will most probably use them, when only null
- which is the default(Nullable<T>)
- has the meaning of "no meaningful value" and 0
already is a valid meaningful value. Otherwise one can use GetValueOrDefault()
or @MillKaDe 's expression from above.
So essentially ?
comes down to != default(T)
- a "verify for non-default value of T"-operator
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 ?t
of the condition checks t
for null (like t != null
or t.HasValue
).
In cases where the null check is followed by a member access, we can already use the existing null-conditional member access t?.Value
.
That means the first '?' (new null check op) in ?t
in line [3d] corresponds to the second '?' (old null-conditional op) in t?.Value
in line [4d]. See marks 'A' above.
The second '?' (new null check op) in ?t.Value
in line [3d] corresponds to the first '?' (new null check op) in ?t?.Value
in line [4d]. See marks 'B' above.
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: 1) Ref => implicit cast to Boolean now allowed (null === false) (probably with restriction to built-in flow constructions if, while, for, ?:, etc.). 2) || and && operators now can work with reference type operands and (null === false)
?
to @lachbaer
I agree that t.GetValueOrDefault
is preferable over ?t?.Value
in a context where it is known that t
is of type Nullable<T>
.
But in some non-nullable context, ?x?.y
(field y
in class x
) might still make sense.
@MillKaDe Yes ?x?.y
of course is valid for explicit field access and really makes sense to me. I even directly understand its represenation, even if it looks a bit awkward on first sight. Just in case of Nullable's GetValueOrDefault
could be better, because it directly states that I am interested in the value of it being null
or unequal to 0
.
@dmitriyse Sorry, I don't understand you. What does ===
mean? Is it a new feature I don't know about?
Mathematical Identity it's usually defined by symbol โก. Just read null โก false or "interpreted as false". I propose not to use any new operator. Just limited implicit cast from null to false. In "if", "while", "for", "?:" and in || and && operators.
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. Disadvantages - limited use cases.
to @dmitriyse
I fear I don't understand your question(s).
1) Implicit cast to bool There is a loooooong list of possible errors in C/C++ because of implicit cast to bool, e.g.
// possible typing error, but legal in C
if (a = b) .. // assigment of b to a, then converted to bool, meaning (b != 0)
// versus most likely intention
if (a == b) .. //
We would like to avoid these kind of bugs. This suggestion tries to block those problems by requiring explicit use of this new check operator.
2) Logical operators ('&&', '||')
The result of the new '?' operator is bool
. So several check can be combined, e.g.
if (?x && !?y) ..
while (?x || ?y) ..
3) Operator '===' C# has no triple equal operator (yet). As far as I know, in JavaScript, '===' is a stricter equal then '==', e.g.
"2" == 2 // is true, cause string "2" has the same 'value' (or maybe lets better call it meaning) as int 2
"2" === 2 // is false, cause their types are different - a string is not an int
but I might be wrong because I don't know much about JavaScript ..
to @dmitriyse
Ah, OK - now I understand a bit more .. But I am pretty sure that the language design team will not allow implicit cast to bool - neither in general nor limited to special places. Some explicitness (for example an operator) is needed to block the errors from C/C++ sneaking in ..
Oh, and there is an aspect we have not discussed yet - should it be possible to overload that new check operator ?
Always ? Never ? Just for some types ?
I think it should not be possible to overload it, especially not for reference types or primitive value types (e.g. the int
types, bool, float, double, decimal)
But what about user defined value types, e.g. struct Point { int x; int y; }
?
Or maybe the new check operator will be limited to refs and primitive value types, because it is considered to be too 'expensive' on non-primitive 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 (?p.x || ?p.y)
(= p.x != 0 || p.y != 0
) ?
@MillKaDe Thanks for you description. I forgot about "if (a=b)" like problems.
@MillKaDe @dmitriyse Exactly, we do not want implicit casting, for many, many good reasons! It is one of the main sources of errors in C/C++ and other languages!
This proposal is about a "is-non-default" operator. Maybe one day there will be an
T operator default(T rhs) { }
; that returns a new instance of any type (value/ref/Nullable).
In that case ?obj
will return true if it is either null
(because ?
somehow stands for a not-null-checking)
or !obj.Equals(default(T))
.
The correct equivalent of ?x
so would be:
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;
@MillKaDe
should it be possible to overload that new check operator ?
No, not in my understanding (see directly above). The 'overloading' would be done with a default
-operator. This way ?
will also work with the new record types.
@lachbaer
About non-zero defaults:
The default value default (T)
is always a bunch of zero bits. The type T
determines the type and the size / number of bits of that bunch of zero bits.
Why ? see this answer by Jon Skeet on stackoverflow.
A class / reference type C
may have a parameterless default constructor, which can set the fields of the constructed object to any desired values. But the default value of the reference to that new object (the address/pointer in C/C++) itself is null (= 0).
In C# a struct / value type S
is not allowed to have a default constructor (which has no parameters). The closest thing to a non-zero default value is :
struct S {
public readonly MyDefaultValueForS = new S (42);
}
but default (S)
must be all zero.
When an array of S S[..]
is allocated, the runtime sets all bits in that array to zero.
There are no calls of an default constructor of S for all the array elements.
Instead the NET runtime does something like memset (s, 0, sizeof (array))
(from C/C++ runtime lib) on the whole array.
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.
So I think there will never be a T operator default(T rhs) { ... }
.
About overloading the new check operator:
Because the default value always is (and always will be) zero bits, the check operator for type T
can always (for all types) compare to default (T)
.
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. It is not so useful for enums and non-integers. And it is not needed at all for bools, cause it would just return the bool itself - but maybe it make sense to define it anyway for symmetry reasons and fewer problems when used in generic types ?
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 System.Object.Equals (System.Object x)
does some automatic memberwise comparison of all fields, when two structs of same type are compared with that Equals
method.
So a compiler generated standard implemention for operator '?' would be technically possible.
But I don't think that it is desperately needed.
But - again - it might make sense for symmetry and simpler use in generic types.
In Roslyn I (also) found
Debug.Assert(!isExtensionMethod || (receiverOpt != null));
Because !
is easily overseen (see #157), it should be either
Debug.Assert((isExtensionMethod == false) || (receiverOpt != null));
throughout the codebase.
Or we have another argument in favor of this proposal, because ?
is not worse than !
.
Debug.Assert(!isExtensionMethod || ?receiverOpt);
I like brevity - as long as it is understandable. So I still like the ?
operator! ๐
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 bool operator ?
that gets called in the following scenarios
if (?secondItem) {
var item = fistItem ?? secondItem;
item?.Execute();
}
In all 3 scenarios the ?
s check for 'null' in the first step and when the custom bool operator ?
exists on the type (value or reference) calls it. Value types can't be 'null', instead the missing of the custom operator on the type makes all 3 statements produce a compile time error.
This would have no impact on current code, even the IL is the same.
See also #545 for specific proposal of ?obj
. Neither has support.
Why not just have the editor change if (obj) to if (obj != null) as autocorrect or AI assisted feature. Reverse, if (!obj) autocorrect to if (obj == null)
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 like