Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

216
Views
Is there a difference between `x is int?` and `x is int` in C#?
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#?

about 4 years ago · Santiago Trujillo
2 answers
Answer question

0

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 operator

The 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 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. 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 occurs

  • If E is a method group or the null literal, of if the type of E 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 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:

    • If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.
    • If T is a nullable type, the result is true if D is the underlying type of T.
    • If T is a non-nullable value type, the result is true if D and T are the same type.
    • Otherwise, the result is false.
about 4 years ago · Santiago Trujillo Report

0

Consider this generic method:

static bool Is<T>(object arg)
{
    return arg is T;
}

The crucial part of this method gets compiled to isinst !!T. Now you would expect Is<int?>(arg) to behave exactly the same way as arg is int?, wouldn't you? To ensure this exact consistency, the C# compiler must emit the same CIL in all the cases and let the burden of handling nullable types lie on the CLR.

The behaviour of the CLR can be viewed in the coreclr source code on GitHub: IsInst, ObjIsInstanceOf. As you can see in the second function, if the type is a nullable representation of the argument type, it returns true.

allow an object of type T to be cast to Nullable (they have the same representation)

Yes, the current behaviour of these instructions is the same, so changing is T? to is T won't make any difference (even for null argument), but to cope with any possible future changes in the CLR, the C# compiler cannot make that decision (although the probability of isinst behaviour changed is close to zero).

Nullable types are really a wondrous thing in .NET, especially due to their particular handling in the CLR, despite the fact that they have no special syntax in CIL (for compatibility). There is really no normal way of boxing a nullable type to its actual type and not the underlying one, as it would raise inconsistencies in casts and checks (is null reference equal to a boxed nullable type null or not?). However, you can trick the CLR into thinking you give it a boxed nullable type (not that you should).

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!