Reputation: 42344
I was just looking at this answer, which contains the code for Nullable<T>
from .NET Reflector, and I noticed two things:
Nullable<T>
to T
.==
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?
Upvotes: 16
Views: 2792
Reputation: 659974
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.
Upvotes: 36
Reputation: 8560
Because the compiler converts Nullable<T>
to T
and then performs the comparison.
Upvotes: 1
Reputation: 21730
Nullable<T>
has this method:
public static implicit operator T?(T value)
{
return new T?(value);
}
It looks like there is an implicit conversion from it 10
to Nullable<int> 10
To make == / != work for you. You can add
public static bool operator ==(MyNullable<T> left, MyNullable<T> right) {}
// together with:
public static implicit operator MyNullable<T>(T value) {}
should give you support for myNullable == 10
operation
Upvotes: 1
Reputation: 26270
Well, if you can use reflector why don't you compile this code:
int? value = 10;
Console.WriteLine(value == 10);
and then open it in reflector? You'll see this (make sure to select 'None' as .net version to decompile to):
int? value;
int? CS$0$0000;
&value = new int?(10);
CS$0$0000 = value;
Console.WriteLine((&CS$0$0000.GetValueOrDefault() != 10) ? 0 : &CS$0$0000.HasValue);
So basically the compiler does the heavy lifting for you. It understands what '==' operation means when used with nullables and compiles the necessary checks accordingly.
Upvotes: 4
Reputation: 70307
This is language dependent. C# and Visual Basic emit different code when dealing with operators on nullable value types. To udnerstand it you need to look at the actual IL code.
Upvotes: 1