Skip to content

Instantly share code, notes, and snippets.

@latkin
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save latkin/20750903fc625b49e27e to your computer and use it in GitHub Desktop.
Save latkin/20750903fc625b49e27e to your computer and use it in GitHub Desktop.
Blog: Null-checking considerations in F#
let nullCheck01 x = (x = null)
public static bool nullCheck01<a>(a x) where a : class
{
return LanguagePrimitives.HashCompare.GenericEqualityIntrinsic<a>(x, default(a));
}
// --- F# core library ---
public static bool GenericEqualityIntrinsic<T>(T x, T y)
{
return LanguagePrimitives.HashCompare.GenericEqualityObj(false, LanguagePrimitives.HashCompare.fsEqualityComparerNoHashingPER, );
}
internal static bool GenericEqualityObj(bool er, IEqualityComparer iec, object xobj, object yobj)
{
if(xobj == null) return yobj == null;
if(yobj != null) { ... core logic, fairly big, prevents JIT inlining ... }
return false;
}
type Cat = { Name : string; Lives : int }
let nullCheck02 (x : Cat) = (x = Unchecked.defaultof<_>)
public static bool nullCheck02(Cat x)
{
Cat obj = null;
// nullref
return x.Equals(obj, LanguagePrimitives.GenericEqualityComparer);
}
type Cat = { Name : string; Lives : int }
let ohNoes x =
try
if x.Lives > 1 then Some({ x with Lives = x.Lives - 1})
else None
with
| :? NullReferenceException -> None
ohNoes (Unchecked.defaultof<_>)
public static FSharpOption<Cat> ohNoes(Cat x)
{
if (x.Lives@ > 1)
{
return FSharpOption<Cat>.Some(new Cat(x.Name@, x.Lives@ - 1));
}
return FSharpOption<Cat>.None;
}
type Cat = { Name : string; Lives : int }
let nullCheck03 x = match x with null -> true | _ -> false
let nullCheck04 (x : Cat) = match box x with null -> true | _ -> false
; if x is null, go to 00D50503
00D504FB test ecx,ecx
00D504FD je 00D50503
; set return value to 0 (false)
00D504FF xor eax,eax
; return
; set return value to 1 (true)
00D50503 mov eax,1
; return
// nullCheck04 is identical except generic "a" becomes concrete "Cat"
.method public static bool nullCheck03<class a> (!!a x) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: box !!a
IL_0006: brfalse.s IL_000a
IL_0008: ldc.i4.0
IL_0009: ret
IL_000a: ldc.i4.1
IL_000b: ret
}
let nullCheck05 (x : 't when 't : null) = System.Object.ReferenceEquals(x, null)
; if x is null, set the lowest byte of return value to 1
; otherwise set it to 0
012404A3 test ecx,ecx
012404A5 sete al
; fill out the rest of return value with 0s
012404A8 movzx eax,al
; return
.method public static bool nullCheck05<class t> (!!t x) cil managed
{
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: box !!t
IL_0007: ldnull
IL_0008: call bool [mscorlib]System.Object::ReferenceEquals(object, object)
IL_000d: ret
}
// --- BCL ---
.method public hidebysig static bool ReferenceEquals (object objA, object objB) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ceq
IL_0004: ret
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment