本文介绍了为什么 None 表示为 null?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

CompilationRepresentationFlags.UseNullAsTrueValue 可以是习惯了

CompilationRepresentationFlags.UseNullAsTrueValue can be used to

允许使用 null 作为可区分联合中的无效鉴别器的表示

Option.None 是最突出的例子.

为什么这很有用?null 检查如何比检查联合情况的传统机制(生成的 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

编辑

我测试了 Jack 的断言,即与 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
  | _ -> ()

结果:

真实: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();

结果都是一样的:

真实: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 继承的方法可能会抛出空引用异常.这是使用 string x/hash x/x=y 而不是 x.ToString() 的一个很好的理由>/x.GetHashCode()/x.Equals(y) 处理 F# 值时.遗憾的是,对于由 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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-14 04:35
查看更多