为什么将None表示为null? [英] Why is None represented as null?

查看:176
本文介绍了为什么将None表示为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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆