Unchecked.defaultof列表

时间:2012-11-22 22:18:42

标签: f#

考虑:

> Unchecked.defaultof<list<int>>;;
val it : int list = null
> 2 :: 1 :: Unchecked.defaultof<list<int>>;;
val it : int list = [2]

这种行为背后的原因是什么?我希望最终列表为[2; 1]或引发异常。

1 个答案:

答案 0 :(得分:28)

这是一个非常了不起的问题。我看了F#列表是如何编译的,它解释了行为。我不认为这是错误(这就是defaultof模块中Unchecked的原因,它可能会烧毁你的计算机!)

首先,空列表[]在F#2.0中没有表示为null,因为那样你就无法在空列表上调用方法 - 这会破坏很多东西(比如将空列表传递给任何Seq函数)。另外,None 表示为null以提高效率,这意味着option<'a>无法实现seq<'a>接口,即使它会有道理。

因此,F#使用特殊值list.Empty来表示空列表。现在,此值是与表示非空列表的值相同类型的实例(类型为FSharpList<'T>)。唯一的区别是实例的headtail字段都是null

现在,您可以看到它的位置 - 空列表基本上编译为this.tail字段为null的对象(this.head字段为{{1也是,但这并不重要)。

null字段为list.Tail时,this.tail属性将抛出异常(因为这意味着您获得了空列表的尾部 - 这是一个错误),但是还有一个内部属性null,它返回值而不进行检查,并用于编译模式匹配。

例如,这是一个简单的list.TailOrNull函数:

iter

这是编译为以下C#代码:

let rec iter (xs:int list) = 
  match list with
  | x::xs -> Console.WriteLine(x); iter xs
  | [] -> ()

通过检查if (list.TailOrNull != null) { Console.WriteLine(list.HeadOrDefault); iter(list.TailOrNull); } 是否为list来检查list.TailOrNull是否为空列表。如果是,则它知道当前null值是表示空列表的list值(并且它不打印其list.Empty,因为它假定这是HeadOrDefault太)。

如果您使用null明确创建defaultof值,则null会为您提供42::nullTailOrNullnullHeadOrDefault的对象{1}}。这就是为什么不打印这个值的原因。

TL; DR - 请勿将42与F#类型一起使用。你永远不知道会发生什么。