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?
Related
This is a weird behaviour that I cannot make sense of. In my example I have a class Sample<T> and an implicit conversion operator from T to Sample<T>.
private class Sample<T>
{
public readonly T Value;
public Sample(T value)
{
Value = value;
}
public static implicit operator Sample<T>(T value) => new Sample<T>(value);
}
The problem occurs when using a nullable value type for T such as int?.
{
int? a = 3;
Sample<int> sampleA = a;
}
Here is the key part:
In my opinion this should not compile because Sample<int> defines a conversion from int to Sample<int> but not from int? to Sample<int>. But it compiles and runs successfully! (By which I mean the conversion operator is invoked and 3 will be assigned to the readonly field.)
And it gets even worse. Here the conversion operator isn't invoked and sampleB will be set to null:
{
int? b = null;
Sample<int> sampleB = b;
}
A great answer would probably be split into two parts:
Why does the code in the first snippet compile?
Can I prevent the code from compiling in this scenario?
You can take a look at how compiler lowers this code:
int? a = 3;
Sample<int> sampleA = a;
into this:
int? nullable = 3;
int? nullable2 = nullable;
Sample<int> sample = nullable2.HasValue ? ((Sample<int>)nullable2.GetValueOrDefault()) : null;
Because Sample<int> is a class its instance can be assigned a null value and with such an implicit operator the underlying type of a nullable object can also be assigned. So assignments like these are valid:
int? a = 3;
int? b = null;
Sample<int> sampleA = a;
Sample<int> sampleB = b;
If Sample<int> would be a struct, that of course would give an error.
EDIT:
So why is this possible? I couldn't find it in spec because it's a deliberate spec violation and this is only kept for backwards compatibility. You can read about it in code:
DELIBERATE SPEC VIOLATION:
The native compiler allows for a "lifted" conversion even when the return type of the conversion not a non-nullable value type. For example, if we have a conversion from struct S to string, then a "lifted" conversion from S? to string is considered by the native compiler to exist, with the semantics of "s.HasValue ? (string)s.Value : (string)null". The Roslyn compiler perpetuates this error for the sake of backwards compatibility.
That's how this "error" is implemented in Roslyn:
Otherwise, if the return type of the conversion is a nullable value type, reference type or pointer type P, then we lower this as:
temp = operand
temp.HasValue ? op_Whatever(temp.GetValueOrDefault()) : default(P)
So according to spec for a given user-defined conversion operator T -> U there exists a lifted operator T? -> U? where T and U are non-nullable value types. However such logic is also implemented for a conversion operator where U is a reference type because of the above reason.
PART 2 How to prevent the code from compiling in this scenario? Well there is a way. You can define an additional implicit operator specifically for a nullable type and decorate it with an attribute Obsolete. That would require the type parameter T to be restricted to struct:
public class Sample<T> where T : struct
{
...
[Obsolete("Some error message", error: true)]
public static implicit operator Sample<T>(T? value) => throw new NotImplementedException();
}
This operator will be chosen as a first conversion operator for nullable type because it's more specific.
If you can't make such a restriction you must define each operator for each value type separately (if you are really determined you can take advantage of reflection and generating code using templates):
[Obsolete("Some error message", error: true)]
public static implicit operator Sample<T>(int? value) => throw new NotImplementedException();
That would give an error if referenced in any place in code:
Error CS0619 'Sample.implicit operator Sample(int?)' is obsolete: 'Some error message'
I think it's lifted conversion operator in action. Specification says that:
Given a user-defined conversion operator that converts from a
non-nullable value type S to a non-nullable value type T, a lifted
conversion operator exists that converts from S? to T?. This lifted
conversion operator performs an unwrapping from S? to S followed by
the user-defined conversion from S to T followed by a wrapping from T
to T?, except that a null valued S? converts directly to a null valued
T?.
It looks like it's not applicable here, because while type S is value type here (int), type T is not value type (Sample class). However this issue in Roslyn repository states that it's actually a bug in specification. And Roslyn code documentation confirms this:
As mentioned above, here we diverge from the specification, in two
ways. First, we only check for the lifted form if the normal form was
inapplicable. Second, we are supposed to apply lifting semantics only
if the conversion parameter and return types are both non-nullable
value types.
In fact the native compiler determines whether to check for a lifted
form on the basis of:
Is the type we are ultimately converting from a nullable value type?
Is the parameter type of the conversion a non-nullable value type?
Is the type we are ultimately converting to a nullable value type, pointer type, or reference type?
If the answer to all those questions is "yes" then we lift to nullable
and see if the resulting operator is applicable.
If compiler would follow specification - it would produce a error in this case as you expect (and in some older versions it did), but now it does not.
So to summarize: I think compiler uses lifted form of your implicit operator, which should be impossible according to specification, but compiler diverges from specification here, because:
It is considered bug in specification, not in compiler.
Specification was already violated by old, pre-roslyn compiler, and it's good to maintain backwards compatibility.
As described in first quote describing how lifted operator works (with addition that we allow T to be reference type) - you may note it describes exactly what happens in your case. null valued S (int?) is assigned directly to T (Sample) without conversion operator, and non-null is unwrapped to int and run through your operator (wrapping to T? is obviously not needed if T is reference type).
Why does the code in the first snippet compile?
A code sample from a source code of Nullable<T> that can be found here:
[System.Runtime.Versioning.NonVersionable]
public static explicit operator T(Nullable<T> value) {
return value.Value;
}
[System.Runtime.Versioning.NonVersionable]
public T GetValueOrDefault(T defaultValue) {
return hasValue ? value : defaultValue;
}
The struct Nullable<int> has an overriden explicit operator as well as method GetValueOrDefault one of these two is used by compiler to convert int? to T.
After that it runs the implicit operator Sample<T>(T value).
A rough picture of what happens is this:
Sample<int> sampleA = (Sample<int>)(int)a;
If we print typeof(T) inside of Sample<T> implicit operator it will display: System.Int32.
In your second scenario compiler doesn't use the implicit operator Sample<T> and simply assigns null to sampleB.
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".
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.
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.
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.