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