The C# language specification doc about the is operator reads:
The result of the operation
E is T, where E is an expression and T is a type, is a boolean value indicating whether E can successfully be converted to type T by a reference conversion, a boxing conversion, or an unboxing conversion. [...]
[...] let D represent the dynamic type of E as follows:
- If the type of E is a reference type, D is the run-time type of the instance reference by E.
- 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 of E.
The result of the operation depends on D and T as follows: [... goes on with the target type]
Then the documentation goes on with a similar distinction about the target type. Since I'm only interested in the expression's type, I that part and also the null cases that are not relevant for this question.
When I have something like that:
object s = "hey";
if (s is string) ...
It's the first case that applies.
When I have something like that:
int? nullableInt = 10;
if (nullableInt is int) ...
It's the second case that applies: E is a nullable type and int is its underlying type. Pretty useless.
But when i have something like that, that is a value boxed into an object, it seems like there is a missing rule.
object o = (int)10;
if (o is int) ...
Here the static type of the expression is object, that is a referece type, while its run-time type is a value type.
This is a very (maybe the most) common scenario for type-testing.
Since the static type of the expression is a reference type, it should be the first case that applies.
But when the 'content' of the reference type is a value type, it should apply rules like 2 and 3 for the boxed value, behavior that I confirmed via test.
But this is not what the documentation says; it seems to me that the boxing case is missing.
Is this analysis correct or it's me missing something?