- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.2k
Description
Description
The following two methods (M1, M2) should behave identically:
public static bool M1<T>(T value) => value is S;
public static bool M2<T>(T value)
{
    if (typeof(T) == typeof(S))
    {
        return true;
    }
    else if (typeof(T) == typeof(S?))
    {
        return Unsafe.As<T, S?>(ref value).HasValue;
    }
    return false;
}However, to my surprise, M1 seems to allocate when T is a nullable struct.
When T is a non-nullable struct, the JIT is smart enough to know the result beforehand.
When T is a nullable struct the resulting code calls System.Runtime.CompilerServices.RuntimeHelpers.Box(System.Runtime.CompilerServices.MethodTable*, Byte ByRef).
To confirm, I profiled the following code:
long x = 0;
S? value = new S();
for (long i = 0; i < long.MaxValue - 1; i++)
{
    x += M1(value) ? 0 : 1;
}
Console.WriteLine(x);I used Visual Studio with .NET Object Allocation Tracking, and it confirmed that M1 allocates while M2 does not.
These observations also hold for a slightly different version (from which I came) but I suppose it has the same underlying issue:
interface I { }
struct S : I { }
static bool M1<T>(T value) => value is I;I believe this simple code definitely should not allocate.
Configuration
Windows 10, .NET 7 and .NET 8.