Is Nullable<T>.Equals Method Implementation Wrong? - c#

I am talking about C# language here.
Definition of Object.Equals(Object) method in msdn is:
Determines whether the specified
Object is equal to the current Object.
If two objects are equal it returns true, however if they are null it returns false:
x.Equals(a null reference (Nothing in
Visual Basic)) returns false.
Why? Because null is not an object.
A NullReferenceException is thrown if the object paremeter is null.
and also we have this:
x.Equals(y) returns the same value as
y.Equals(x).
No problem at all till here. It is very similar to Java. But C# also provides a System.Nullable struct for non-nullable types. As far as I know, a struct is an object. It inherits Object.Equals method.
If I have a struct like this:
struct Car
{
public string Make;
public string Model;
public uint Year;
public Car(string make, string model, uint year)
{
Make = make;
Model = model;
Year = year;
}
}
And create four instances:
Car car1 = new Car("make", "model", 2009);
Car car2 = new Car("make", "model", 2009);
Car car3 = new Car("make", "model", 2008);
car1.Equals(car2); // will return true
car1.Equals(car3); // will return false;
And as far as I know we can't set a struct to a null value. But System.Nullable is a struct and we can do compile this without any errors:
int? i = null;
(I hope that someone can explain this also. Is it a struct or something else?)
My real question is:
i.Equals(null); // returns true!
(Normally x.Equals(y) = y.Equals(x) Of course null.Equals(i) is not valid here... )
Obviously Object.Equals method is overridden here. Maybe it is documented and this is specified. But is this approach correct/nice ? If so what is the difference between == and Equals method for Nullable values?

I think your confusion is rooted in the following line
i? = null;
This does not actually create a null value variable. It's essentially syntatic sugar for the following
Nullable<int> i = new Nullable<int>();
The resulting property HasValue on i will have the value false. It is not null but instead a value type with empty values. Or just an empty nullable. IMHO, the best way to think of this is that null is convertible to an empty Nullable<T> for any given T.
Knowing that it makes the line i.Equals(null) a bit easier to understand. It's syntatic sugar for the following
Nullable<int> i = new Nullable<int>();
i.Equals(null);
The type Nullable<T> only overrides Equals(object). The implementation of this method though considers a null value to be Equal to an empty nullable value. So it's behaving correctly.

To answer your side question, Nullable is a struct with a T: Struct constraint. So, even though int? i = null; is null, i is an instance of the Nullable struct.

Related

How to determine if a runtime object is of a nullable value type

First: this is not a duplicate of How to check if an object is nullable?. Or, at least, there was no useful answer provided to that question, and the author's further elaboration actually asked how to determine whether a given type (eg. as returned from MethodInfo.ReturnType) is nullable.
However, that is easy. The hard thing is to determine whether a runtime object whose type is unknown at compile time is of a nullable type. Consider:
public void Foo(object obj)
{
var bar = IsNullable(obj);
}
private bool IsNullable(object obj)
{
var type = obj.GetType();
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
// Alternatively something like:
// return !(Nullable.GetUnderlyingType(type) != null);
}
This does not work as intended, because the GetType() call results in a boxing operation (https://msdn.microsoft.com/en-us/library/ms366789.aspx), and will return the underlying value type, rather than the nullable type. Thus IsNullable() will always return false.
Now, the following trick uses type parameter inference to get at the (unboxed) type:
private bool IsNullable<T>(T obj)
{
var type = typeof(T);
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
This seems initially promising. However, type parameter inference only works when the type of the object is known at compile time. So:
public void Foo(object obj)
{
int? genericObj = 23;
var bar1 = IsNullable(genericObj); // Works
var bar2 = IsNullable(obj); // Doesn't work (type parameter is Object)
}
In conclusion: the general problem is not to determine whether a type is nullable, but to get at the type in the first place.
So, my challenge is: how to determine if a runtime object (the obj parameter in the above example) is nullable? Blow me away :)
Well, you're too late. A boxed value no longer has the type information you seek - boxing a int? value results either in null, or a boxed int. In a way, it's an analogue to calling GetType on null - it doesn't really make sense, there's no type information.
If you can, stick with generic methods instead of boxing as much as possible (dynamic can be very helpful for some of the interfaces between actual nullable values and objects). If you can't, you'll have to use your own "boxing" - or even just creating your own Nullable-like type, that will be a class rather than a very hacky struct-like thing :D
If needed, in fact, you can even make your Nullable type a struct. It's not the struct-ness that breaks the type information - it's not the boxing itself that destroys that information, it's the CLR hacks that enable Nullable to match the performance of non-nullable values. It's very smart and very useful, but it breaks some reflection-based hacks (like the one you're trying to do).
This works as expected:
struct MyNullable<T>
{
private bool hasValue;
private T value;
public static MyNullable<T> FromValue(T value)
{
return new MyNullable<T>() { hasValue = true, value = value };
}
public static implicit operator T (MyNullable<T> n)
{
return n.value;
}
}
private bool IsMyNullable(object obj)
{
if (obj == null) return true; // Duh
var type = obj.GetType().Dump();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(MyNullable<>);
}
Doing the same with System.Nullable doesn't; even just doing new int?(42).GetType() gives you System.Int32 instead of System.Nullable<System.Int32>.
System.Nullable not a real type - it gets special treatment by the runtime. It does stuff that you simply can't replicate with your own types, because the hack isn't even in the type definition in IL - it's right in the CLR itself. Another nice abstraction leak is that a nullable type is not considered a struct - if your generic type constraint is struct, you can't use a nullable type. Why? Well, adding this constraint means it's legal to use e.g. new T?() - which wouldn't be possible otherwise, since you can't make a nullable of a nullable. It's easy with the MyNullable type I've written here, but not with the System.Nullable.
EDIT:
The relevant part of the CLI specification (1.8.2.4 - Boxing and unboxing a value):
All value types have an operation called box. Boxing a value of any
value type produces its boxed value; i.e., a value of the
corresponding boxed type containing a bitwise copy of the original
value. If the value type is a nullable type—defined as an
instantiation of the value type System.Nullable—the result is a
null reference or bitwise copy of its Value property of type T,
depending on its HasValue property (false and true, respectively). All
boxed types have an operation called unbox, which results in a managed
pointer to the bit representation of the value.
So by definition, the box operation on nullable types produces either a null reference or the stored value, never a "boxed nullable".

How is it possible for me to call HasValue on a null object? [duplicate]

This question already has answers here:
Why does Nullable<T> HasValue property not throw NullReferenceException on Nulls?
(5 answers)
Closed 6 years ago.
Consider the following code:
int? myInt = null;
var hasValue = myInt.HasValue;
This is perfectly acceptable, and HasValue will return false. But how have I managed to reference a property on an object that is null? The following wouldn't work:
public class MyClass
{
public bool MyBool { get { return true; } };
}
// Elsewhere in code...
MyClass myObject = null;
myObject.MyBool;
This would raise a null reference exception. The fact I'm comparing the two code snippets above as though they are equal tells me that I'm misunderstanding a structure or some syntax somewhere.
Can somebody please tell me why code snippet 1 does not raise an exception, but why my code snippet 2 would?
In the first case, you have a value of type Nullable<int>, which is "the null value for the type". The Nullable<T> type is a value type with two fields, like this:
public struct Nullable<T> where T : struct
{
private T value;
private bool hasValue;
}
When you use the null literal with a Nullable<T>, you're really just creating a value where the value field has the default value of T, and hasValue is false. Using the HasValue property isn't following a reference - it's just getting a field out of the struct.
In the second case, the value of myObject is a null reference... so if you try to use the MyBool property, that fails because it's trying to find an object when there isn't one.
So the key points are:
Nullable<T> is a struct, not a class
The value of a variable of type Nullable<T> is just a value with two fields, not a reference
The null literal just means "the null value of the relevant type", it does not always mean a null reference.

Type checking on Nullable<int>

If have the following method:
static void DoSomethingWithTwoNullables(Nullable<int> a, Nullable<int> b)
{
Console.WriteLine("Param a is Nullable<int>: " + (a is Nullable<int>));
Console.WriteLine("Param a is int : " + (a is int));
Console.WriteLine("Param b is Nullable<int>: " + (b is Nullable<int>));
Console.WriteLine("Param b is int : " + (b is int));
}
When i call this method with null as a parameter, the type check returns false for this parameter. For example this code
DoSomethingWithTwoNullables(5, new Nullable<int>());
results in this output:
Param a is Nullable<int>: True
Param a is int : True
Param b is Nullable<int>: False
Param b is int : False
Is there any way to preserve the type information when using a Nullable and passing null? I know that checking the type in this example is useless, but it illustrates the problem. In my project, I pass the parameters to another method that takes a params object[] array and tries to identify the type of the objects. This method needs to do different things for Nullable<int> and Nullable<long>.
Going straight to the underlying problem, no, you can't do this. A null Nullable<int> has exactly the same boxed representation as a null Nullable<long>, or indeed a 'normal' null. There is no way to tell what 'type' of null it is, since its underlying representation is simply all-zeros. See Boxing Nullable Types for more details.
conceptually, new Nullable<int> is null.
If we generalise, forgetting about Nullable<T>:
string s = null;
bool b = s is string;
we get false. false is the expected value for a type-check on a null value.
You can try using Reflection to achieve this. Relevant article here.
Unfortunately, null does not point to any specific memory location, and thus there is no metadata that you can associate with it to lookup the type. Thus, you cannot gain any additional information about the variable.
Unless I'm misunderstanding the question, you can get the type using GetType(). For example,
int? myNullableInt = null;
Console.WriteLine(myNullableInt.GetValueOrDefault().GetType());
If myNullableInt is null, a default value will be returned. Check the type of this returned value and, in this case, it will return System.Int32. You can do an If..else/Switch check on the returned type to perform the relevant action.
(int? is the same as Nullable<int>)
You can't do this, nor should you want to. Since Nullable<T> is a struct, value-type variables of this type have all the type information you need at compile time. Just use the typeof operator.
On the other hand, you might have a Nullable instance whose type you don't know at compile time. That would have to be a variable whose static type is object or some other reference type. Howver, because a Nullable<T> value boxes to a boxed T value, there's no such thing as a boxed Nullable<T>. That instance whose type your checking will just be a T.
This is why you get the same result for is int and is Nullable<int>. There's no way to distinguish between a boxed int and a boxed int?, because there is no boxed int?.
See Nulls not missing anymore for details.
As it has already been pointed out null has no type. To figure out if something is int? vs long? you need to use reflection to get information about something storing the type. Here is some code that you may be able to use as inspiration (not knowing exactly what you try to achieve the code is a bit weird):
class Pair<T> where T : struct {
public Pair(T? a, T? b) {
A = a;
B = b;
}
public T? A { get; private set; }
public T? B { get; private set; }
}
void DoSomething<T>(Pair<T> pair) where T : struct {
DoMore(pair);
}
void DoMore(params object[] args) {
Console.WriteLine("Do more");
var nullableIntPairs = args.Where(IsNullableIntPair);
foreach (Pair<int> pair in nullableIntPairs) {
Console.WriteLine(pair.A);
Console.WriteLine(pair.B);
}
}
bool IsNullableIntPair(object arg) {
var type = arg.GetType();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(Pair<>)
&& type.GetGenericArguments()[0] == typeof(int);
}
If you execute the following code
DoSomething(new Pair<int>(5, new int?()));
DoSomething(new Pair<long>(new long?(), 6L));
you get the following output:
Do more
5
null
Do more
You can use typeof :
a == typeof(Nullable<int>) //true
a == typeof(int) //false

The '.' operator on null nullable

How is it that you can access null nullable's propery HasValue?
I looked to the compiled code, and it's not a syntactic sugar.
Why this doesn't throw NullReferenceException:
int? x = null;
if (x.HasValue)
{...}
That's because int? is short for Nullable<int> which is a value type, not a reference type - so you will never get a NullReferenceException.
The Nullable<T> struct looks something like this:
public struct Nullable<T> where T : struct
{
private readonly T value;
private readonly bool hasValue;
//..
}
When you assign null there is some magic happening with support by the compiler (which knows about Nullable<T> and treats them special in this way) which just sets the hasValue field to false for this instance - which is then returned by the HasValue property.
Like BrokenGlass said, an int? is actually a Nullable<T>.
Structures always contain a value. Usually you cannot set a structure variable to null, but in this special case you can, essentially setting it to default(Nullable<T>). This sets its contents to null rather than the variable itself.
When you set a Nullable<T> to a value, it uses an implicit operator to set Value = value to the new value and HasValue = true.
When you set Nullable<T> to null, it nulls all of the structure's fields. For a bool field such as HasValue, null == false.
Since a Nullable<T> variable is a structure, the variable can always be referenced because its contents is null rather than the variable itself.
There's more information on structures in the Remarks section of the MSDN page struct.

? (nullable) operator in C# [duplicate]

This question already has answers here:
What is the purpose of a question mark after a value type (for example: int? myVariable)?
(9 answers)
Closed 5 years ago.
What is changed by applying nullable Operator on value type datatype that now it can store null.
As others have said, "?" is just shorthand for changing it to Nullable<T>. This is just another value type with a Boolean flag to say whether or not there's really a useful value, or whether it's the null value for the type. In other words, Nullable<T> looks a bit like this:
public struct Nullable<T>
{
private readonly bool hasValue;
public bool HasValue { get { return hasValue; } }
private readonly T value;
public T value
{
get
{
if (!hasValue)
{
throw new InvalidOperationException();
}
return value;
}
}
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
// Calling new Nullable<int>() or whatever will use the
// implicit initialization which leaves value as default(T)
// and hasValue as false.
}
Obviously in the real code there are more methods (like GetValueOrDefault()) and conversion operators etc. The C# compiler adds lifted operators which effectively proxy to the original operators for T.
At the risk of sounding like a broken record, this is still a value type. It doesn't involve boxing... and when you write:
int? x = null;
that's not a null reference - it's the null value of Nullable<int>, i.e. the one where hasValue is false.
When a nullable type is boxed, the CLR has a feature whereby the value either gets boxed to a null reference, or a plain boxed T. So if you have code like this:
int? x = 5;
int y = 5;
object o1 = x;
object o2 = y;
The boxed values referred to by o1 and o2 are indistinguishable. You can't tell that one is the result of boxing a nullable type.
The ? syntax is syntactic sugar to the Nullable<T> struct.
In essence, when you write int? myNullableInt, the compiler changes it to Nullable<int> myNullableInt.
From MSDN (scroll down to "Nullable Types Overview"):
The syntax T? is shorthand for Nullable, where T is a value type. The two forms are interchangeable.
Nothing is changed on the value type itself, it's simply wrapped in a System.Nullable<T> struct.
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
The type changes from what it used to be to a Nullable type.
If you had an int, and decided to make it an int?:
int myInt;
And you make it:
int? myInt;
it is now:
Nullable<int> myInt;
In reality.
In basic terms, a nullable type is a boxed version of the normal type that has an extra boolean field called hasValue on it.
When this field is set to false, then the instance is null.
Check out this answer for a bit more detail on the CLR implementation, and why they might have chosen it: Boxing / Unboxing Nullable Types - Why this implementation?

Categories

Resources