为什么将None表示为null? [英] Why is None represented as null?
问题描述
CompilationRepresentationFlags.UseNullAsTrueValue
可用于
允许将null用作已歧视联合中无效标识符的表示形式
Permit the use of null as a representation for nullary discriminators in a discriminated union
Option.None
是最突出的示例.
这为什么有用?空检查比传统的检查并集情况(生成的Tag
属性)的机制有什么优势?
Why is this useful? How is a null check better than the traditional mechanism for checking union cases (the generated Tag
property)?
它可能导致意外的行为:
It leads to perhaps unexpected behavior:
Some(1).ToString() //"Some(1)"
None.ToString() //NullReferenceException
编辑
我测试了杰克的断言,即与null相比,将其与静态只读字段进行比较会更快.
EDIT
I tested Jack's assertion that comparing to null instead of a static readonly field is faster.
[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type T<'T> =
| Z
| X of 'T
let t = Z
使用ILSpy,我可以看到t
编译为null(按预期):
Using ILSpy, I can see t
compiles to null (as expected):
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
| _ -> ()
结果:
Real:00:00:00.036,CPU:00:00:00.046,GC gen0:0,gen1:0,gen2:0
Real: 00:00:00.036, CPU: 00:00:00.046, GC gen0: 0, gen1: 0, gen2: 0
如果删除了CompilationRepresentation
属性,则t
变为静态只读字段:
If the CompilationRepresentation
attribute is removed, t
becomes a static readonly field:
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();
结果相同:
Real:00:00:00.036,CPU:00:00:00.031,GC gen0:0,gen1:0,gen2:0
Real: 00:00:00.036, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
模式匹配在前一种情况下编译为t == null
,在后一种情况下编译为t is Z
.
The pattern match is compiled as t == null
in the former case and t is Z
in the latter.
推荐答案
Jack的答案似乎不错,但要扩大一点,在IL级别,CLR提供了用于加载空值(ldnull
)且高效的特定操作码.测试它们的方法(ldnull
后跟beq
/bne.un
/ceq
/cgt.un
).当JITted时,它们应该比取消引用Tag
属性并相应地分支更有效.虽然每次通话节省的费用可能很小,但由于经常使用期权类型,因此累积节省的费用可能很大.
Jack's answer seems good, but to expand a little bit, at the IL level the CLR provides a specific opcode for loading null values (ldnull
) and efficient means of testing for them (ldnull
followed by beq
/bne.un
/ceq
/cgt.un
). When JITted, these should be more efficient than dereferencing a Tag
property and branching accordingly. While the per-call savings are probably small, option types are used frequently enough that the cumulative savings may be significant.
当然,您要注意一个折衷:从obj
继承的方法可能会引发null引用异常.这是处理F#值时使用string x
/hash x
/x=y
而不是x.ToString()
/x.GetHashCode()
/x.Equals(y)
的一个很好的理由.遗憾的是,对于null
表示的值,没有(可能)等价于x.GetType()
.
Of course, as you note there is a tradeoff: methods inherited from obj
may throw null reference exceptions. This is one good reason to use string x
/hash x
/x=y
instead of x.ToString()
/x.GetHashCode()
/x.Equals(y)
when dealing with F# values. Sadly, there is no (possible) equivalent of x.GetType()
for values represented by null
.
这篇关于为什么将None表示为null?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!