.method public static void Test<class T>(object A_0) cil managed
{
// Code size 13 (0xd)
.maxstack 1
.locals init (!!T V_0)
IL_0000: ldarg.0
IL_0001: isinst !!T
IL_0006: unbox.any !!T
IL_000b: stloc.0
IL_000c: ret
} // end of method DemoType::Test
The equal C# code is:
public static void Test<T>(object o) where T : class
{
T t = o as T;
}
My questions are:
Why unbox.any been called? if you just do
var a = father as child
isinst intruction will call and no unbox.any, and If i'll remove the generic definition and i'll try to cast (isinst) the object to some class, no unbox.any will be called.
Maybe unbox.any been called because the generic definition, so in this case the unbox.any need to throw a NullReferenceException because the answer of isinst instruction return null for this casting. see unbox_any. And if you try to run this code you will see that no exception has thrown.
Update
I can understand the unbox_any becuase the object type parameter and it try to cast it to concrete type after the isinst check. Maybe the generics influence also.
My question is, why not thrown an exception in unbox.any if the the obj we try to unbox to T is null?
The documentation say: "NullReferenceException is thrown if obj is a null reference."
The unbox is to keep the verifier happy. The verifier is not particularly smart about knowing that a type parameter T is always going to be a reference type, and so the C# compiler emits these otherwise unnecessary unboxes.
If you do a search of the Roslyn source code for Unbox_any and IsVerifierReference you'll see that this happens in quite a few places around the code generator.
The jitter will know when generating the code whether the type parameter is a reference or not and should generate decent code regardless of the seemingly unnecessary instruction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With