Negation of null for Nullable<bool> - c#

I am confused with c# compiler in case of negating the null value of bool?. Compiler does interpret !null as null. My expectation is to raise
CS0266 (Cannot implicitly convert type 'bool?' to 'bool')
Sample code:
bool? nullableVal = null;
//if (nullableVal) //OK: CS0266 bool? can't be converted to bool
// ;
var expectCS0266 = !nullableVal;//No compiler error/warning
//if ((!null) ?? false)//OK: CS8310 Operator '!' cannot be applied to operands of type "<NULL>"
// ;
if (! nullableVal ?? false)
;//this statement isn't reached, because of precedence of ! is higher than ??
//and !null == null
if (!(nullableVal ?? false))
;//this statement is reached, OK
Can somebody prove why the compiler is right or vice versa.

See Section 7.3.7 of the spec:
Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:
For the unary operators
+ ++ - -- ! ~
a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single ? modifier to the operand and result types. The lifted operator produces a null value if the operand is null. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.
(Emphasis mine)
So:
bool? x = null;
bool? y = !x;
Follows this rule. We're using the lifted form of the unary ! operator, which results in null if the value it's applied to is null.
!null isn't allowed, because null isn't of type Nullable<T>. !(bool?)null works, however (although it produces a compiler warning).
! indeed has higher precedence than ??, see Section 7.3.1

Related

Why `as` is giving null for nullable value types? [duplicate]

This question already has an answer here:
Casting an object using the "as" keyword returns null
(1 answer)
Closed 3 years ago.
long? i = 10000;
int? s = i as int?;
Why is it giving me a null in s variable? I know that casting it using int? s = (int?)i; will work fine but why can't I use an as operator?
From MSDN:
The code is equivalent to the following expression except that the expression variable is evaluated only one time.
expression is type ? (type)expression : (type)null
Since i is int? is false the as keyword will return (int?)null.
The types aren't equivalent and you get null, that is just how as works
However, you could just direct Cast
long? i = 10000;
var s = (int?)i;
// or even better make sure its *checked*
var s = checked((int?)i);
Why does this work?
C# Language Specification 11.6.1 Nullable Conversions
...
Evaluation of a nullable conversion based on an underlying conversion
from S to T proceeds as follows:
If the nullable conversion is from S? to T?:
If the source value is null (HasValue property is false), the result is the null value of type T?.
Otherwise, the conversion is evaluated as an unwrapping from S? to S, followed by the underlying conversion from S to T, followed by a
wrapping from T to T?.
...
as does not do this, it checks the run-time types, if they are not equal returns null T?
Additional Resources
checked (C# Reference)
The checked keyword is used to explicitly enable overflow checking for
integral-type arithmetic operations and conversions.
#Update from comments
I got that we can't convert and why we can't do I but why they are
suggesting it is there any scenario where it will be useful i'e some
random image link of Compiler Error CS0077 "The as operator must be used with a reference type or nullable type ('int' is a non-nullable value type)."
The reason is (in your image example) a value type can't be null it could never work with it. The thing you are missing is there are more complex examples of what could happen, you can define implicit operators on custom types, etc.
The compiler cannot implicitly convert 'long' to 'int'
And as per MSDN
The as operator is like a cast operation. However, if the conversion
isn't possible, as returns null instead of raising an exception.
ref: as (C# Reference)
So with your code, conversion is not possible, as is returning Null.

Safe Navigation Operator with Any()

Hey Everyone I thought that this code would be valid in any case because it is casted to bool
parameter.Request?.InnerData?.Any() == false
Instead this code is casted to type bool?
Can tell me someone why? (this is main question)
Is there better way to check this instead of something like this?
var isThereInnerData = parameter.Request?.InnerData?.Any();
if (isThereSegments == null || isThereSegments == false)
With a null-conditional operator, null propagates. You can't end up with a non-nullable type if you use the null-conditional operator - this is the same behaviour in languages where null operations are "safe" by default (SQL, Objective-C, ...). So the result is default(bool?) if parameter.Request is null, or parameter.Request.InnerData is null. Otherwise, you get true or false, but by using the null-conditional operator you already assume the result can in fact be null, so the compiler must accomodate that. Since bool can't be null, it is changed into a nullable bool (bool?).
The solution is simple - think about what logical value a null should have, and use that:
if (parameter.Request?.InnerData?.Any() ?? true)
In this case, a null value is interpreted as true, but you can also use false if you want. Alternatively, instead of the null-coalescing operator, you can use GetValueOrDefault, whatever feels better for you.
Here is what C# reference says about ?. operator:
x?.y – null conditional member access. Returns null if the left hand
operand is null.
Since at compile time the compiler doesn't knows whether the expression will evaluate to a non-null value, the compiler infers the type as Nullable<bool>
Since you are chaining the ?. operator this line from msdn documentation is relevant too:
The null-condition operators are
short-circuiting. If one operation in a chain of conditional member
access and index operation returns null, then the rest of the chain’s
execution stops. Other operations with lower precedence in the
expression continue. For example, E in the following always executes,
and the ?? and == operations execute.
bool is a value type and per default cannot be compared with null. C# converts your return value to bool? to give you the option to support both.

How does `Nullable<T> t = null` work? [duplicate]

The Nullable<T> type is defined as a struct. In .Net, you can't assign null to a struct because structs are value types that cannot be represented with null (with the exception of Nullable<T>).
int i = null; // won't compile - we all know this
int? i = null; // will compile, and I'm glad it does, and it should compile, but why?
How did Nullable<T> become an exception to the rule "You can't assign null to a value type?" The decompiled code for Nullable<T> offers no insights as of to how this happens.
How did Nullable<T> become an exception to the rule "You can't assign null to a value type?"
By changing the language, basically. The null literal went from being "a null reference" to "the null value of the relevant type".
At execution time, "the null value" for a nullable value type is a value where the HasValue property returns false. So this:
int? x = null;
is equivalent to:
int? x = new int?();
It's worth separating the framework parts of Nullable<T> from the language and CLR aspects. In fact, the CLR itself doesn't need to know much about nullable value types - as far as I'm aware, the only important aspect is that the null value of a nullable value type is boxed to a null reference, and you can unbox a null reference to the null value of any nullable value type. Even that was only introduced just before .NET 2.0's final release.
The language support mostly consists of:
Syntactic sugar in the form of ? so int? is equivalent to Nullable<int>
Lifted operators
The changed meaning of null
The null-coalescing operator (??) - which isn't restricted to nullable value types

Why doesn't incrementing Nullable<int> throw an exception?

Could you please explain, why does Console.WriteLine write empty line (Console.WriteLine(null) give me compilation error) and why there isn't NullReferenceException (even a+=1 shouldn't raise it)?
int? a = null;
a++; // Why there is not NullReferenceException?
Console.WriteLine(a); // Empty line
You're observing the effects of a lifted operator.
From section 7.3.7 of the C# 5 specification:
Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:
For the unary operators + ++ - -- ! ~
a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single ? modifier to the operand and result types. The lifted operator produces a null value if the operand is null. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result.
So basically, a++ in this case is an expression with a result of null (as an int?) and the variable is left untouched.
When you call
Console.WriteLine(a);
that's being boxed into object, which converts it to a null reference, which is printed as an empty line.
Jon's answer is correct but I would add some additional notes.
Why does Console.WriteLine(null) give a compilation error?
There are 19 overloads of Console.WriteLine and three of them are applicable to a null: the one that takes a string, the one that takes a char[] and the one that takes an object. C# cannot determine which of these three you mean, so it gives an error. Console.WriteLine((object)null) would be legal because now it is clear.
why does Console.WriteLine(a) write an empty line?
a is a null int?. Overload resolution chooses the object version of the method, so the int? is boxed to a null reference. So this is basically the same as Console.WriteLine((object)null), which writes an empty line.
Why there is not NullReferenceException on the increment?
Where's the null reference that you are worried about? a is a null int? which is not a reference type to begin with! Remember, nullable value types are value types, not reference types, so don't expect them to have reference semantics unless they are boxed to a reference type. There is no boxing in the addition.
Are You incrementing null???
int? a = null;
a++;
This statement simply means null++ i.e. null+1.
As per this document, A nullable type can represent the correct range of values for its underlying value type, plus an additional null value.A Nullable, pronounced "Nullable of Int32," can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value
Here you are incrementing null, then also it will become null value not 0 or any other integer.
Why it prints blank instead of error??
when you print a nullable type with null value it prints blank instead of error because you are printing a variable i.e. value of a memory location. which maybe null or any integer.
But when you try to print null using Console.WriteLine(null), as null is not a variable, so it doesn't refer to any memory location. And hence it gives error "NullReferenceException".
Then how can you print any integer using Console.WriteLine(2); ??
In this case, 2 will gets in memory at temporary location, and the pointer points to that memory location to print.

Null-Coallescing Operator - Why Casting?

Can anyone please tell me why does the first of the following statements throws a compilation error and the second one does not?
NewDatabase.AddInParameter(NewCommand, "#SomeString", DbType.String, SomeString ?? DBNull.Value); // <-- Throws compilation error!
NewDatabase.AddInParameter(NewCommand, "#SomeString", DbType.String, (object)(SomeString) ?? DBNull.Value); // <-- Compiles!
I tried other nullable types such as byte? and got the same result. Can anyone please tell me why do I need to cast to object first?
You need to tell the compiler what type to use. The result type of the null coalescing operator has to be the same as one of the operand types (or the underlying type of the first operand, if it's a nullable value type, in some cases). It doesn't try to find a "most specific type which both operands can be converted to" or anything like that.
For the details of how the language is defined when it comes to the null coalescing operator, see the C# 4 language specification, section 7.13:
The type of the expression a ?? b depends on which implicit conversions are available on the operands. In order of preference, the type of a ?? b is A0, A, or B, where A is the type of a (provided that a has a type), B is the type of b (provided that b has a type), and A0 is the underlying type of A if A is a nullable type, or A otherwise.
The first example fails because SomeString and DBValue.Null are not implicitly interchangable types.
That is because the type on the right-hand side of the null-coalescing operator must be implicitly convertible to the type on the left hand side (or vice versa). For your first example, the types involved are string and DBNull. These types are unrelated, so conversion fails.
DBValue.Null is not a string; it's an Object. .NET will not implicitly cast to Object in expressions; it must be explicitly told that you are expecting an Object result.
Because the expression needs to have a single return type. Since String and DbValue can't be cast to one another, the compiler can't figure out which type return do you want. When you cast to Object, you're giving the compiler a type it can cast to.

Categories

Resources