Is the C# compiler optimizing nullable types? - c#

Can anybody shed any light on why this unit test is failing in Visual Studio 2013?
[TestMethod]
public void Inconceivable()
{
int? x = 0;
Assert.AreEqual(typeof(int?), x.GetType());
}

Your test is failing because:
Calling GetType on a Nullable type causes a boxing operation to be performed when the type is implicitly converted to Object. Therefore GetType always returns a Type object that represents the underlying type, not the Nullable type.
You can read more from How to: Identify a Nullable Type.
Some examples taken from the previous article:
int? i = 5;
Type t = i.GetType();
Console.WriteLine(t.FullName); //"System.Int32"
Also note that:
The C# is operator also operates on a Nullable's underlying type. Therefore you cannot use is to determine whether a variable is a Nullable type. The following example shows that the is operator treats a Nullable<int> variable as an int.
int? i = 5;
if (i is int) { ... } // true
You are correct in presuming that the C# compiler is optimizing nullable types. Here's a quote from Jon Skeet's C# in Depth which should answer your question:
It’s only with respect to boxing and unboxing that the CLR has
any special behavior regarding nullable types. In fact, the behavior was only changed shortly before the release of .NET 2.0, as the result of community requests.
An instance of Nullable is boxed to either a null reference (if it doesn’t have a value) or a boxed value of T (if it does). It never boxes to a “boxed nullable int”—there’s no such type.
There's a similar thread on StackOverflow: Nullable type is not a nullable type?

Related

Type int? vs type int

I've this comparison which equals false as expected
bool eq = typeof(int?).Equals(typeof(int));
now I have this code
List<object> items = new List<object>() { (int?)123 };
int result = items.OfType<int>().FirstOrDefault();
but this returns 123 - anyway that value is of type int?
How can this be?
Nullable types have special "boxing" rules; "boxing" is when a value-type is treated as object, as per your code. Unlike regular value-types, a nullable value-type is boxed either as null (regular null, no type), or as the non-nullable type (the T in T?). So: an int? is boxed as an int, not an int?. Then when you use OfType<int>() on it, you get all the values that are int, which is: the single value you passed in, since it is of type int.
A nullable value type is boxed by the following rules
If HasValue returns false, the null reference is produced.
If HasValue returns true, a value of the underlying value type T is
boxed, not the instance of nullable.
In your example second rule has been followed as you have value:
var i = (object)(int?)123;
It is a bit late, but beside of Marc's answer to your question, I want to give some additional information about Nullable value types in CLR.
The CLR has built-in support for nullable value types. This special support is provided for boxing, unboxing, calling GetType, calling interface methods.
For example, let's check GetType():
Int32? x = 5;
Console.WriteLine(x.GetType());
What you think it will print to the console?
System.Nullable<Int32? Not, the result is System.Int32.
Or, let's check boxing, which you noted in your question:
Int32? n =5;
Object o = n;
Console.WriteLine("o's type={0}", o.GetType()); // "System.Int32"
The rule is that:
When the CLR is boxing a Nullable instance, it checks to see if it
is null, and if so, the CLR doesn’t actually box anything, and null is
returned. If the nullable instance is not null, the CLR takes the
value out of the nullable instance and boxes it. In other words, a
Nullable with a value of 5 is boxed into a boxed-Int32 with a
value of 5.
And, at the end I want to explain how CLR add special support for calling interface methods from Nullable Types.
Let's take a look to that:
Int32? n = 5;
Int32 result = ((IComparable) n).CompareTo(5); // Compiles & runs OK
Console.WriteLine(result); // 0
In the preceding code, I’m casting n, a Nullable<Int32>, to IComparable<Int32>, an interface
type. However, the Nullable<T> type does not implement the IComparable<Int32> interface as
Int32 does. The C# compiler allows this code to compile anyway.

How to "customize" the Name of a type, like Nullable?

How does the Nullable class do it?
public class Foo<T>
{
public T Bar { get; set; }
}
var n1 = new Nullable<int>(123).GetType().Name; // "Int32"
var n2 = new Foo<int>().GetType().Name; // "Foo`1"
Is it possible to make Foo have the same type name, i.e. "Int32"?
tl;dr: No, you can't change the name of Foo. What you're seeing with Nullable is a side-effect of how nullable values are boxed.
There are a few things at play causing the behavior you see regarding the Nullable type:
Invoking .GetType() on a value type causes the value to be boxed.
Nullable gets special treatment by the runtime regarding boxing. A Nullable itself is never boxed, instead:
If the Nullable contains a value, that value is boxed (the int value 123 in this case).
If the Nullable does not contain a value, the boxing operation evaluates as null.
The result is that, in your code, Object.GetType() is seeing a boxed int value when it executes, not a boxed Nullable<int> object (because, again, that's impossible). This is why the name of a nullable is the same as the name of its content. This is the simplest way to demonstrate what's happening:
new Nullable<int>(123).GetType() == typeof(int)
new Nullable<int>(123).GetType() != typeof(Nullable<int>)
As a side note, because a nullable without a value boxes as null, it's an error to invoke .GetType() on a null nullable! This simultaneously makes sense (it's null, right?) and doesn't make sense (it's a value type... it can't really be null!). When a nullable has no value, you can invoke methods implemented or overridden on Nullable itself (because these calls do not require boxing), but you cannot invoke methods inherited from Object and not overridden.
Nullable has a strange dual personality, and sometimes it results in surprises like this.
Now that we know what's going on with the Nullable case, we can see that Nullable doesn't actually rename itself -- the name of the type that you see is the result of nullable boxing behavior.

Is there a technical reason for no implicit conversion from DBNull to nullable types?

Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
Is there a technical reason why there is no implicit conversion from DBNull to the various nullable and/or sql types? I understand why the conversions don't currently happen, but don't understand why an implicit conversion wasn't created at the time or added in subsequent versions of the framework.
Just to be clear, I'm looking for technical reasons, not "because that's they way they did it" or "I like it that way".
The most straightforward explanation comes from Microsoft's documentation:
CLR nullable types are not intended for storage of database nulls because an ANSI SQL null does not behave the same way as a null reference (or Nothing in Visual Basic).
Well, I don't know about the SqlTypes case, but there definitely are some technical reasons why adding an implicit conversion between DBNull.Value and values of Nullable<T> with HasValue = false wouldn't work.
Remember, DBNull is a reference type, and despite the fact that Nullable<T> acts like a reference type -- by pretending to be able to take on the null value -- it's actually a value type, with value semantics.
In particular, there's a weird edge case when values of type Nullable<T> are boxed. The behavior is special-cased in the runtime to box values of type Nullable<T> to a boxed version of T, not a boxed version of Nullable<T>.
As the MSDN documentation explains it:
When a nullable type is boxed, the common language runtime automatically boxes the underlying value of the Nullable(Of T) object, not the Nullable(Of T) object itself. That is, if the HasValue property is true, the contents of the Value property is boxed. When the underlying value of a nullable type is unboxed, the common language runtime creates a new Nullable(Of T) structure initialized to the underlying value.
If the HasValue property of a nullable type is false, the result of a boxing operation is Nothing. Consequently, if a boxed nullable type is passed to a method that expects an object argument, that method must be prepared to handle the case where the argument is Nothing. When Nothing is unboxed into a nullable type, the common language runtime creates a new Nullable(Of T) structure and initializes its HasValue property to false.
Now we get into a tricky problem: the C# language spec (§4.3.2) says we can't use an unboxing conversion to convert DBNull.Value into Nullable<T>:
For an unboxing conversion to a given nullable-type to succeed at run-time, the value of the source operand must be either null or a reference to a boxed value of the underlying non-nullable-value-type of the nullable-type. If the source operand is a reference to an incompatible object, a System.InvalidCastException is thrown.
And we can't use a user-defined conversion to convert from object to Nullable<T>, either, according to §10.10.3:
It is not possible to directly redefine a pre-defined conversion. Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types.
OK, you or I couldn't do it, but Microsoft could just amend the spec, and make it legal, right? I don't think so.
Why? Well, imagine the intended use case: you've got some method that is specified to return object. In practice, it either returns DBNull.Value or int. But how could the compiler know that? All it knows is that the method is specified to return object. And the conversion operator to be applied must be selected at compile time.
OK, so assume that there is some magical operator that can convert from object to Nullable<T>, and the compiler has some way of knowing when it is applicable. (We don't want it to used for every method that is specified to return object -- what should it do if the method actually returns a string?) But we still have an issue: the conversion could be ambiguous! If the method returns either long, or DBNull.Value, and we do int? v = Method();, what should we do when the method returns a boxed long?
Basically, to make this work as intended, you'd have to use the equivalent of dynamic to determine the type at runtime and convert based on the runtime type. But then we've broken another rule: since the actual conversion would only be selected at runtime, there's no guarantee that it would actually succeed. But implicit conversions are not supposed to throw exceptions.
So at this point, it's not only a change to the specified behavior of the language, a potentially significant performance hit, and on top of that it could throw an unexpected exception! That seems like a pretty good reason not to implement it. But if you need one more reason, remember that every feature starts out minus 100 points.
In short: what you really want here can't be done with an implicit conversion anyway. If you want the behavior of dynamic, just use dynamic! This does what you want, and is already implemented in C# 4.0:
object x = 23;
object y = null;
dynamic dx = x;
dynamic dy = y;
int? nx = (int?) dx;
int? ny = (int?) dy;
Console.WriteLine("nx.HasValue = {0}; nx.Value = {1}; ny.HasValue = {2};",
nx.HasValue, nx.Value, ny.HasValue);

What C# data types can be nullable types?

Can someone give me a list, or point me to where I can find a list of C# data types that can be a nullable type?
For example:
I know that Nullable<int> is ok
I know that Nullable<byte[]> is not.
I'd like to know which types are nullable and which are not. BTW, I know I can test for this at runtime. However, this is for a code generator we're writing, so I don't have an actual type. I just know that a column is string or int32 (etc).
All value types (except Nullable<T> itself) can be used in nullable types – i.e. all types that derive from System.ValueType (that also includes enums!).
The reason for this is that Nullable is declared something like this:
struct Nullable<T> where T : struct, new() { … }
A type is said to be nullable if it can be assigned a value or can be assigned null, which means the type has no value whatsoever. Consequently, a nullable type can express a value, or that no value exists. For example, a reference type such as String is nullable, whereas a value type such as Int32 is not. A value type cannot be nullable because it has enough capacity to express only the values appropriate for that type; it does not have the additional capacity required to express a value of null.
The Nullable structure supports using only a value type as a nullable type because reference types are nullable by design.
The Nullable class provides complementary support for the Nullable structure. The Nullable class supports obtaining the underlying type of a nullable type, and comparison and equality operations on pairs of nullable types whose underlying value type does not support generic comparison and equality operations.
From Help Docs
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
It can be any value type including struct, it cannot be a reference type, as those are inherently nullable already.
Yes:
Int32
double
DateTime
CustomStruct
etc.
No:
string
Array
CustomClass
etc.
For more information, see MSDN: http://msdn.microsoft.com/en-us/library/2cf62fcy(v=VS.80).aspx
Any data type can be nullable.
Nullable works for you because normal int is not nullable, so the conversion to nullable int is necessary.
Nullable<int[]> not works because int[] is already a nullable type.
Nullable does not support types that are already nullable.
Note, that you need to convert a type to be nullable only if he is not already a nullable type.
Value types are not nullable, value types are int, float, double, long etc, as well as structs you create.
While reference types, for example, are already nullable (classes for example).
You can use the ? operator to make the type to be a nullable type.
Note, that even tho it's not a good practice if you would use the question mark operator on a type that is already nullable it will not raise an error.
int? myNullableVar = null;
int[]? MyNullableArr = null; //not necessary but does not raise an error

Do C# Nullable variables still function as value types?

If I declare a nullable (either via Nullable or the ? symbol) variable from a value type, does it still respect the rules of value types (i.e. pass by value by default, deallocated when it falls out of scope, not when the garbage collector runs) or does it turn into a reference type since it's actually of type Nullable?
The documentation is here:
http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx
As you can see, the documentation describes this as the "nullable structure", indicating that it is a value type. Also, the documentation gives the first lines of the declaration of the type:
public struct Nullable<T> where T : struct, new()
again showing that the type is a value type.
Yes, System.Nullable is a generic Struct, ie value type.
Nullable value types are essentially just a struct wrapper around a value type which allows them to be flagged as null.
something like this:
struct Nullable<T>
{
public T Value;
public bool HasValue;
}
I believe it's actually more complex than that under the hood, and the compiler does lots of nice stuff for you so you can write for example if(myInt == null). But yes, they are still value types.

Categories