Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
You might have already read
this: VS2005 made the last-minute DCR related to boxed Nullable<T>.
Runtime now treats Nullable<T> differently from other generic value types
when boxing:
Int32? x = null;
object y = x;
// y is simply null, not a truly boxed "nullable<int> struct" (which then
tells it has no value)
And
Int32 ? x = 100;
object y = x;
// y is boxed Int32 100 (same as "object y = 100"), no longer
a boxed "nullable<int> struct" with a field of value 100.
Reflection late-bind invocation always deals with "object"; value type instances
are always getting boxed first before invocation. One of such APIs: PropertyInfo.SetValue(Object
obj, Object value, Object[]
index). There was
one question in Microsoft Technical Forums: Exception is thrown when setting
DateTime value to a property with type Nullable<DateTime>. With this DCR change,
such late-bind calls can be written in a more natural way.
class C
{
Int32? m_field;
public Int32?
Property
{
get { return
m_field; }
set { m_field =
value; }
}
}
PropertyInfo pi = typeof(C).GetProperty("Property");
C c = new C();
pi.SetValue(c, 100, null);
// works
pi.SetValue(c, (Int32?)100, null);
// no longer have to write like this
After boxing, 100 and (Int32?)100 are the same thing: boxed Int32 object. There
is no more boxed object with type "Int32?"; late-invocation has to accept it and
set to Int32? property. Or think it this way, runtime can not tell where a boxed
Int32 object was originally from: either just Int32 instance or Int32? instance;
it is reasonable to allow setting a boxed Int32 object to field/property of either
Int32 or Int32? type (same for argument pass-in when doing method invocation)
pi.SetValue(c, null, null);
// works
This actually worked before the DCR. And ,
Object ret = pi.GetValue(c, null);
ret will be either boxed Int32 object or simply null. There is no problem in casting
the result to Int32? type.
Here are some reflection behavior changes as a result of this DCR:
Probably the most important one is "typeof(T?).IsAssignableFrom(typeof(T)) == true".
PropertyInfo.SetValue/GetValue use this first to ensure the pass-in value is assignable
to Nullable<T> property.Object.GetType() will never get back any type of Nullable<T>. Joe had an interesting
post on this.
Activator.CreateInstance could never be expected to return null before; with this
DCR, it will return null when creating instance of type Nullable<T> but not
providing non-null T value. For example, Activator.CreateInstance(typeof(Int32?))
returns null.
Comments
- Anonymous
August 23, 2005
Haibo Luo has a nice description of the changes made by Microsoft to nullable types and boxing for .NET 2.0 RTM: http://blogs.msdn.com/haibo_luo/archive/2005/08/23/455241.aspx This is good news for Sooda, because it would require very little work to properly support nullable types... - Anonymous
August 23, 2005
The comment has been removed - Anonymous
August 28, 2005
Hi barrkel - I talked to Peli (http://blog.dotnetwiki.org/), who tested this for JIT. He will give you an answer to your question. - Anonymous
August 29, 2005
The following snippet should answer naturally both questions
static void Main()
{
int? i = 1;
object o = i; // A
int? j = (int?)o; // B
int k = (int)o; // C
}
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 32 (0x20)
.maxstack 2
.locals init (valuetype [mscorlib]System.Nullable1<int32> V_0,<br> object V_1,<br> valuetype [mscorlib]System.Nullable
1<int32> V_2,
int32 V_3)
IL_0000: nop
IL_0001: ldloca.s V_0
IL_0003: ldc.i4.1
IL_0004: call instance void valuetype [mscorlib]System.Nullable1<int32>::.ctor(!0)<br> IL_0009: nop<br> IL_000a: ldloc.0<br> IL_000b: box valuetype [mscorlib]System.Nullable
1<int32> // A
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: unbox.any valuetype [mscorlib]System.Nullable`1<int32> // B
IL_0017: stloc.2
IL_0018: ldloc.1
IL_0019: unbox.any [mscorlib]System.Int32 // C
IL_001e: stloc.3
IL_001f: ret
} // end of method Program::Main