Dlaczego brak jest reprezentowany jako null?
CompilationRepresentationFlags.UseNullAsTrueValue
może być użyte do
Zezwól na użycie wartości null jako reprezentacji dyskryminatorów zerowych w dyskryminowanym związku
Option.None
jest najbardziej widocznym tego przykładem.
Dlaczego jest to przydatne? W jaki sposób sprawdzanie wartości zerowej jest lepsze niż tradycyjny mechanizm sprawdzania przypadków unii (generowanyTag
własność)?
Prowadzi to do nieoczekiwanego zachowania:
Some(1).ToString() //"Some(1)"
None.ToString() //NullReferenceException
EDYTOWAĆPrzetestowałem twierdzenie Jacka, że porównywanie do pustego zamiast statycznego pola tylko do odczytu jest szybsze.
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type T<'T> =
| Z
| X of 'T
let t = Z
Korzystając z ILSpy, widzęt
kompiluje do wartości null (zgodnie z oczekiwaniami):
public static Test.T<a> t<a>()
{
return null;
}
Test:
let mutable i = 0
for _ in 1 .. 10000000 do
match t with
| Z -> i <- i + 1
| _ -> ()
Wyniki:
Real: 00: 00: 00.036, CPU: 00: 00: 00.046, GC gen0: 0, gen1: 0, gen2: 0
JeśliCompilationRepresentation
atrybut został usunięty,t
staje się statycznym polem tylko do odczytu:
public static Test.T<a> t<a>()
{
return Test.T<a>.Z;
}
public static Test.T<T> Z
{
[CompilationMapping(SourceConstructFlags.UnionCase, 0)]
get
{
return Test.T<T>._unique_Z;
}
}
internal static readonly Test.T<T> _unique_Z = new Test.T<T>._Z();
A wyniki są takie same:
Real: 00: 00: 00.036, CPU: 00: 00: 00.031, GC gen0: 0, gen1: 0, gen2: 0
Dopasowanie wzorca jest kompilowane jakot == null
w pierwszym przypadku it is Z
w tym drugim.