Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the == operator work for Nullable<T> when == is not defined?

I was just looking at this answer, which contains the code for Nullable<T> from .NET Reflector, and I noticed two things:

  1. An explicit conversion is required when going from Nullable<T> to T.
  2. The == operator is not defined.

Given these two facts, it surprises me that this compiles:

int? value = 10;
Assert.IsTrue(value == 10);

With the code value == 10, either value is being magically converted to an int (hence allowing int's == operator to be used, or the == operator is being magically defined for Nullable<int>. (Or, I presume less likely, Reflector is leaving out some of the code.)

I would expect to have to do one of the following:

Assert.IsTrue((value.Equals(10)); // works because Equals *is* defined
Assert.IsTrue(value.Value == 10); // works because == is defined for int
Assert.IsTrue((int?)value == 10); // works because of the explicit conversion

These of course work, but == also works, and that's the part I don't get.

The reason I noticed this and am asking this question is that I'm trying to write a struct that works somewhat similarly to Nullable<T>. I began with the Reflector code linked above, and just made some very minor modifications. Unfortunately, my CustomNullable<T> doesn't work the same way. I am not able to do Assert.IsTrue(value == 10). I get "Operator == cannot be applied to operands of type CustomNullable<int> and int".

Now, no matter how minor the modification, I would not expect to be able to do...

CustomNullable<T> value = null;

...because I understand that there is some compiler magic behind Nullable<T> that allows values to be set to null even though Nullable<T> is a struct, but I would expect I should be able to mimic all the other behaviors of Nullable<T> if my code is written (almost) identically.

Can anyone shed light on how the various operators of Nullable<T> work when they appear not to be defined?

like image 911
devuxer Avatar asked Jan 26 '12 01:01

devuxer


People also ask

Can we compare int with null?

Some int value as an int? is definitely non-null and null is definitely null. The compiler realizes that and since a non-null value is not equal to a definite null value, the warning is given. The compiler also optimizes this away because it is always false. It won't even load the x variable at all.

What is null forgiving operator?

By using the null-forgiving operator, you inform the compiler that passing null is expected and shouldn't be warned about. You can also use the null-forgiving operator when you definitely know that an expression cannot be null but the compiler doesn't manage to recognize that.

How do you access the underlying value of a nullable type?

If you want to use the default value of the underlying value type in place of null , use the Nullable<T>. GetValueOrDefault() method. At run time, if the value of a nullable value type is null , the explicit cast throws an InvalidOperationException.

Does HasValue check for null?

The HasValue property returns true if the variable contains a value, or false if it is null. You can only use == and != operators with a nullable type. For other comparison use the Nullable static class.


1 Answers

Given these two facts, it surprises me that this compiles

Given only those two facts, that is surprising.

Here's a third fact: in C#, most operators are 'lifted to nullable'.

By "lifted to nullable", I mean that if you say:

int? x = 1;
int? y = 2;
int? z = x + y;

then you get the semantics of "if either x or y is null then z is null. If both are not null then add their values, convert to nullable, and assign the result to z."

The same goes for equality, though equality is a bit weird because in C#, equality is still only two-valued. To be properly lifted, equality ought to be three-valued: x == y should be null if either x or y is null, and true or false if x and y are both non-null. That's how it works in VB, but not in C#.

I would expect I should be able to mimic all the other behaviors of Nullable<T> if my code is written (almost) identically.

You are going to have to learn to live with disappointment because your expectation is completely out of line with reality. Nullable<T> is a very special type and its magical properties are embedded deeply within the C# language and the runtime. For example:

  • C# automatically lifts operators to nullable. There's no way to say "automatically lift operators to MyNullable". You can get pretty close by writing your own user-defined operators though.

  • C# has special rules for null literals -- you can assign them to nullable variables, and compare them to nullable values, and the compiler generates special code for them.

  • The boxing semantics of nullables are deeply weird and baked into the runtime. There is no way to emulate them.

  • Nullable semantics for the is, as and coalescing operators are baked in to the language.

  • Nullables do not satisfy the struct constraint. There is no way to emulate that.

  • And so on.

like image 171
Eric Lippert Avatar answered Sep 22 '22 19:09

Eric Lippert