File tree Expand file tree Collapse file tree 1 file changed +17
-3
lines changed Expand file tree Collapse file tree 1 file changed +17
-3
lines changed Original file line number Diff line number Diff line change @@ -53,16 +53,30 @@ public static ref T DangerousGetValueOrDefaultReference<T>(this ref T? value)
53
53
public static unsafe ref T DangerousGetValueOrNullReference < T > ( ref this T ? value )
54
54
where T : struct
55
55
{
56
+ ref T resultRef = ref Unsafe . NullRef < T > ( ) ;
57
+
58
+ // This pattern ensures that the resulting code ends up having a single return, and a single
59
+ // forward branch (the one where the value is null) that is predicted non taken. That is,
60
+ // the initial null ref is very cheap as it's just clearing a register, and the rest of the
61
+ // code is a single assignment (lea on x86-64) that should always be taken. This results in:
62
+ // =============================
63
+ // L0000: xor eax, eax
64
+ // L0002: cmp byte ptr[rcx], 0
65
+ // L0005: je short L000b
66
+ // L0007: lea rax, [rcx + 4]
67
+ // L000b: ret
68
+ // =============================
69
+ // This is better than what the code would've been with two separate returns in the method.
56
70
if ( value . HasValue )
57
71
{
58
72
#if NET7_0_OR_GREATER
59
- return ref Unsafe . AsRef ( in Nullable . GetValueRefOrDefaultRef ( in value ) ) ;
73
+ resultRef = ref Unsafe . AsRef ( in Nullable . GetValueRefOrDefaultRef ( in value ) ) ;
60
74
#else
61
- return ref Unsafe . As < T ? , RawNullableData < T > > ( ref value ) . Value ;
75
+ resultRef = ref Unsafe . As < T ? , RawNullableData < T > > ( ref value ) . Value ;
62
76
#endif
63
77
}
64
78
65
- return ref Unsafe . NullRef < T > ( ) ;
79
+ return ref resultRef ;
66
80
}
67
81
68
82
#if ! NET7_0_OR_GREATER
You can’t perform that action at this time.
0 commit comments