考虑一下:
> Unchecked.defaultof<list<int>>;;
val it : int list = null
> 2 :: 1 :: Unchecked.defaultof<list<int>>;;
val it : int list = [2]这种行为背后的原因是什么?我希望最终的列表是2;1,或者引发一个异常。
发布于 2012-11-23 09:01:27
这是一个非常令人惊讶的问题。我研究了F#列表是如何编译的,它解释了这种行为。我不会认为这是错误(这就是为什么defaultof在Unchecked模块中,它可能会烧毁您的计算机!)
首先,在F# 2.0中,空列表[]没有表示为null,因为这样就不能调用空列表上的方法了--这会破坏很多东西(比如将空列表传递给任何Seq函数)。另外,为了提高效率,None值被表示为null,这意味着option<'a>不能实现seq<'a>接口,即使它是有意义的。
因此,F#使用一个特殊的值list.Empty来表示空列表。现在这个值是与表示非空列表的值具有相同类型的实例(类型为FSharpList<'T>)。唯一的区别是实例的head和tail字段都是null。
现在,您也许可以看到这是怎么回事了--一个空列表实际上被编译成一个对象,其中的this.tail字段为null ( this.head字段也为null,但这并不重要)。
当字段this.tail为null时,list.Tail属性将抛出异常(因为这意味着您将获得一个空列表的尾部--这是一个错误),但是还有一个内部属性list.TailOrNull,它返回不进行检查的值,并用于编译模式匹配。
例如,下面是一个简单的iter函数:
let rec iter (xs:int list) =
match list with
| x::xs -> Console.WriteLine(x); iter xs
| [] -> ()这将被编译为以下C#代码:
if (list.TailOrNull != null) {
Console.WriteLine(list.HeadOrDefault);
iter(list.TailOrNull);
}这将通过检查其list.TailOrNull是否为null来检查list是否为空列表。如果是,那么它知道当前的list值是表示空列表的list.Empty值(并且它不打印它的HeadOrDefault,因为它假定这也是null )。
如果您使用defaultof显式地创建null值,那么42::null将提供一个对象,其中TailOrNull为null,而HeadOrDefault为42。这就是为什么不打印这个值的原因。
TL;DR -不要将Unchecked.defaultof与F#类型一起使用。你永远不会知道会发生什么。
https://stackoverflow.com/questions/13520720
复制相似问题