class C<T> where T : struct {
bool M1(object o) => o is T;
bool M2(object o) => o is T?;
}
The two methods above seems to behave equally, both when passing null
reference or boxed T
value. However, the generated MSIL code is a bit different:
.method private hidebysig instance bool M1(object o) cil managed {
.maxstack 8
IL_0000: ldarg.1
IL_0001: isinst !T
IL_0006: ldnull
IL_0007: cgt.un
IL_0009: ret
}
vs
.method private hidebysig instance bool M2(object o) cil managed {
.maxstack 8
IL_0000: ldarg.1
IL_0001: isinst valuetype [mscorlib]System.Nullable`1<!T>
IL_0006: ldnull
IL_0007: cgt.un
IL_0009: ret
}
As you may see, the o is T?
expression actually performs type check for Nullable<T>
type, despite the fact that nullable types are specially handled by CLR so that C# represents boxed T?
value as null
reference (if T?
has no value) or boxed T
value. It seems impossible to get box of Nullable<T>
type in pure C# or maybe even in C++/CLI (since runtime handles box
opcode to support this "T?
=> T
box / null
" boxing).
Am I missing something or o is T?
is practically equivalent to o is T
in C#?
According to the spec (emphasis mine), in E is T
, non-nullable value types of T
and corresponding nullable types are handled the same way:
7.10.10 The
is
operatorThe
is
operator is used to dynamically check if the run-time type of an object is compatible with a given type. The result of the operationE is T
, whereE
is an expression andT
is a type, is a boolean value indicating whetherE
can successfully be converted to typeT
by a reference conversion, a boxing conversion, or an unboxing conversion. The operation is evaluated as follows, after type arguments have been substituted for all type parameters:
If
E
is an anonymous function, a compile-time error occursIf
E
is a method group or the null literal, of if the type ofE
is a reference type or a nullable type and the value of E is null, the result is false.Otherwise, let
D
represent the dynamic type ofE
as follows:
- If the type of
E
is a reference type,D
is the run-time type of the instance reference byE
.If the type of
E
is a nullable type,D
is the underlying type of that nullable type.If the type of
E
is a non-nullable value type,D
is the type ofE
.The result of the operation depends on
D
andT
as follows:
- If
T
is a reference type, the result is true ifD
andT
are the same type, ifD
is a reference type and an implicit reference conversion fromD
toT
exists, or ifD
is a value type and a boxing conversion fromD
toT
exists.- If
T
is a nullable type, the result is true ifD
is the underlying type ofT
.- If
T
is a non-nullable value type, the result is true ifD
andT
are the same type.- Otherwise, the result is false.