Consider the following code:
int? x = null; Console.Write ("Hashcode: "); Console.WriteLine(x.GetHashCode()); Console.Write("Type: "); Console.WriteLine(x.GetType());
When executed, it writes that Hashcode is 0
, but fails with NullReferenceException
in attempt to determine type of x
. I know that methods called on nullable types are actually called on underlying values, so I expected program to fail during x.GetHashCode()
.
So, what is the fundamental difference between those two methods and why doesn't the first of them fail?
It's usually better to avoid a NullReferenceException than to handle it after it occurs. Handling an exception can make your code harder to maintain and understand, and can sometimes introduce other bugs. A NullReferenceException is often a non-recoverable error.
Characteristics of Nullable TypesNullable types can only be used with value types. The Value property will throw an InvalidOperationException if value is null; otherwise it will return the value. The HasValue property returns true if the variable contains a value, or false if it is null. You can only use == and !=
Nullable reference types are a new feature in C# 8.0. They allow you to spot places where you're unintentionally dereferencing a null value (or not checking it.) You may have seen these types of checks being performed before C# 8.0 in ReSharper's Value and Nullability Analysis checks.
When the nullable type is boxed, the underlying value type is stored in the object, rather than an instance of the nullable type itself. For example, if we box int?, the boxed value will store an int.
This is because int? x = null;
essentially creates an instance of the value type System.Nullable<int>
, with an "inner" null
value (you can check it via .HasVaue
Property). When GetHashCode
is invoked, the override Nullable<int>.GetHashCode
is the method candidate (since the method is virtual), now we have an instance of Nullable<int>
, and execute its instance method, perfect.
When invoking GetType
, the method is non-virtual, so the instance of Nullable<int>
is boxed to System.Object
first, according to the document, and boxed value is null
, hence the NullReferenceException
.
To clarify Danny Chen's correct answer:
Nullable<T>
is a value type. The value type consists of a bool, which indicates nullity (false means null) and a T, the value.Nullable<T>
. They box to either a boxed T
or a null reference.S
is implemented as though it has an invisible ref S
argument; that is how this
is passed.C
is implemented as if there was an invisible C
argument; that is how this
is passed.Now you have enough information to deduce what happens. GetHashCode
is virtual and overridden by Nullable<T>
so when you call it, you call it as though there was an invisible ref Nullable<T>
argument for this
. No boxing happens.
GetType
is not virtual and so cannot be overridden and is defined on object
. Therefore it expects an object
for this
, When called on a Nullable<T>
the receiver must be boxed, and therefore can box to null, and therefore can throw.
If you called ((object)x).GetHashCode()
then you'd see an exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With