CompilationRepresentationFlags.UseNullAsTrueValue
можно использовать для
Разрешить использование null в качестве представления для нулевых дискриминаторов в разграниченном объединении
Option.None
является наиболее ярким примером этого.
Почему это полезно? Как нулевая проверка лучше, чем традиционный механизм проверки случаев объединения (сгенерированное свойство Tag
)?
Это приводит к неожиданному поведению:
Some(1).ToString() //"Some(1)"
None.ToString() //NullReferenceException
ИЗМЕНИТЬ
Я тестировал утверждение Джека, что сравнение с нулевым, а не статическим полем readonly выполняется быстрее.
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type T<'T> =
| Z
| X of 'T
let t = Z
Используя ILSpy, я вижу, что t
компилируется в null (как и ожидалось):
public static Test.T<a> t<a>()
{
return null;
}
Тест:
let mutable i = 0
for _ in 1 .. 10000000 do
match t with
| Z -> i <- i + 1
| _ -> ()
Результаты:
Реал: 00: 00: 00.036, CPU: 00: 00: 00.046, GC gen0: 0, gen1: 0, gen2: 0
Если атрибут CompilationRepresentation
удален, t
становится статическим полем readonly:
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();
И результаты те же:
Реал: 00: 00: 00.036, CPU: 00: 00: 00.031, GC gen0: 0, gen1: 0, gen2: 0
Соответствие шаблону компилируется как t == null
в первом случае и t is Z
в последнем.