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

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.

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.

Is the C# compiler optimizing nullable types?

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?

Why does .ToString() on a null string cause a null error, when .ToString() works fine on a nullable int with null value?

selectedItem has two fields:
int? _cost
string _serialNumber
In this example, _cost and _serialNumber of selectedItem are BOTH null. I am reading through the fields of selectedItem via their properties, and filling in textboxes with their values, when...
TextBox1.Text = selectedItem.Cost.ToString(); //no error
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error
I understand that SerialNumber.ToString() is redundant (because it is already a string), but I don't understand why this causes this exception:
Nullable object must have a value.
int? _cost is nullable, and does not have a value, yet it does not give me the exception.
string _serialNumber is nullable, and does not have a value, yet it does give me the exception.
This question touches on it, the guy is essentially asking the same thing, but there is no designated answer, and it also doesn't explain why a nullable int? For example, can I use .ToString() on a nullable int but not on a null string?
Because string type's null really points to nothing, there isn't any object in memory.But int? type(nullable) even with value set to null still points to some object.If you read Jeffrey Richter's "CLR via C#" you'll find out that nullable type are just facade classes for common types with some incapsulated logics in order to make work with DB null more convenient.
Check msdn to learn about nullable types.
A Nullable<int> is a struct and can't really be null. So a method call on a "null" struct still works.
There is some "compiler magic" that makes _cost == null a valid expression.
int? is not actually an object in its own but it's a Nullable<int> object.
So when you declare int? _Cost, you are actually declaring Nullable<int> _Cost and the property of _Cost.Value is undefined not the _Cost object itself.
It is actually a syntactic sugar to use non nullable types like int, bool or decimal easily.
According to MSDN:
The syntax T? is shorthand for System.Nullable<T>, where T is a value type. The two forms are interchangeable.
A string is a reference type, but a nullable int is a value type. Here is a Good discussion of the differences http://www.albahari.com/valuevsreftypes.aspx.
The Nullable is actually a struct exposing two properties: HasValue and Value. If you do this you will get your error:
int? i = null;
i.Value.ToString()
In order to check whether or not your int? has a value you can access i.HasValue
The reason is simple. int? or Nullable<int> is a struct or a value type, it can never be null.
So what happens when we do:
int? _cost = null;
_cost will have two fields Value and HasValue, when we assign null to _cost its HasValue flag will be set to false and the Value field would be assigned default(T) in case of int? it would 0.
Now when we call ToString on _cost, Nullable<T> has an override definition of ToString, which if we look at Microsoft's provided Source Reference is implemented like:
public override string ToString() {
return HasValue ? value.ToString() : "";
}
Thus it returns an empty string, since _cost is assigned null.
Now comes the case of string _serialNumber. Being string it is a reference type and it can purely hold null. If it is holding null then calling ToString on it would produce the Null Reference Exception as expected.
You may see: Value Types and Reference Types - MSDN
what i think the reason is, when the compiler encounters a primitive data type it wraps it, to its corresponding object. The toString() method call is just an indirect call(wrapping and then calling the method) here and the exception is handled there.
While in the case of String, we are directly calling the method. When pointing to a null, the method throws the exception.
TextBox2.Text = selectedItem.SerialNumber.ToString(); //error
yiels error because it's calling function ToString() which is member of System.String. This function returns this instance of System.String; no actual conversion is performed. Also, String is a reference type. A reference type contains a pointer to another memory location that holds the data.
TextBox1.Text = selectedItem.Cost.ToString(); //no error
yields no error because it's calling to function ToString() which is a member of System.Integer. This function converts the numeric value of this instance to its equivalent string representation. Also, Integer is a value type. A data type is a value type if it holds the data within its own memory allocation.
The same function name ToString() but performs different task.
String.ToString Method
Int32.ToString Method
Value types and reference types

Why can an int? set to null have instance properties?

I'm curious as to why the following code works (run under the VS debugger):
int? x = null;
null
x.HasValue
false
If x is indeed null, what instance does HasValue refer to? Is HasValue implemented as an extension method, or does the compiler special case this to make it magically work?
Because x isn't a reference type. The ? is just syntactic sugar for Nullable<T>, which is a struct (value type).
int? is actually a structure Nullable<int>. Hence this, your x cannot be null, because it is always instance of a structure.
Hand-waving answer: Nullable structs are magic.
Longer answer: Null is not actually what is represented by the value. When you assign null to a nullable struct, what you will see happen behind the scenes is different.
int? val = null; // what you wrote
Nullable<Int32> val = new Nullable<Int32>(); // what is actually there
In this case, an instance of the struct is created that has the T Value set to a default value and the bool HasValue property set to false.
The only time you will actually obtain a null reference from a Nullable<T> is when it is boxed, as a Nullable<T> boxes directly to T or null, depending upon if the value is set.
There are several meanings to null.
One in programming languages which present variables and memory in a pointer-based manner (which includes C#'s references though it hides some of the details) is "this doesn't point to anything".
Another is "this has no meaningful value".
With reference types, we often use the former to represent the latter. We might use string label = null to mean "no meaningful label. It remains though that it's still also a matter of what's going on in terms of what's where in memory and what's pointing to it. Still, it's pretty darn useful, what a shame we couldn't do so with int and DateTime in C#1.1
That's what Nullable<T> provides, a means to say "no meaningful value", but at the level below it's not null in the same way a null string is (unless boxed). It's been assigned null and is equal to null so it's logically null and null according to some other semantics, but it's not null in the "doesn't point to anything" implementation difference between reference and value types.
It's only the "doesn't point to anything" aspect of reference-type null that stops you from calling instance methods on it.
And actually, even that isn't strictly true. IL let's you call instance methods on a null reference and as long as it doesn't interact with any fields, it will work. It can't work if it needs (directly or indirectly) those fields since they don't exist on a null refernce, but it could call null.FineWithNull() if that method was defined as:
int FineWithNull()
{
//note that we don't actually do anything relating to the state of this object.
return 43;
}
With C# it was decided to disallow this, but it's not a rule for all .NET (I think F# allows it, but I'm not sure, I know unmanaged C++ allowed it and it was useful in some very rare cases).
When using int? x = null then x is assigned a new instance of Nullable<int> and ist value is set to null.
I don't exactly know the internals but I would assume that the assignment operator itself is somewhat overloaded.
A nullable type isn't actually null since it still doesn't get around the fact that value types can't be null. It is, instead, a reference to the Nullable<> struct (which is also a value type and can't be null).
More information here.
Basically, you're always referring to an instance of something when you use a nullable type. The default information returned by that reference is the stored value (or null if there is no stored value).
Nullable isn't really a reference type, and its instance methods are one of the places where this shows up. Fundamentally, it is a struct type containing a boolean flag and a value of the type it is a nullable of. The languages special-case various operators [to be lifting, or to consider (bool?)null false in some cases] and the null literal, and the runtime special-cases boxing, but apart from that it's just a struct.
It's a completely new type. Nullable is not T.
What you have is a generic class something like this:
public struct Nullable<T>
{
public bool HasValue { get { return Value != null; } }
public T Value { get; set; }
}
I'm sure there's more to it (particularly in the getter and setter, but that's it in a nutshell.
The nullable type (in this case: nullable int) has a property of HasValue which is boolean. If HasValue is True, the Value property (of Type T, in this case, an int) will have a valid value.

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

Categories