考虑:
> Unchecked.defaultof<list<int>>;;
val it : int list = null
> 2 :: 1 :: Unchecked.defaultof<list<int>>;;
val it : int list = [2]
Run Code Online (Sandbox Code Playgroud)
这种行为背后的原因是什么?我希望最终列表为[2; 1]或引发异常.
Tom*_*cek 28
这是非常了不起的问题.我看了F#列表是如何编译的,它解释了行为.我不认为这是错误(这就是为什么defaultof在Unchecked模块中,它可能会烧毁你的计算机!)
首先,空列表[]没有null在F#2.0中表示,因为那时你将无法在空列表上调用方法 - 这会破坏很多东西(比如将空列表传递给任何Seq函数).顺便说一句,该None值被表示为null提高效率,这意味着option<'a>无法实现的seq<'a>,即使它将使Sense界面.
因此,F#使用特殊值list.Empty来表示空列表.现在,此值是与表示非空列表(类型为FSharpList<'T>)的值相同类型的实例.唯一的区别是实例的两个head和tail字段都是null.
现在,您可以看到它的发展方向 - 空列表实际上是编译到this.tail字段所在的对象null(并且this.head字段也是null如此,但这并不重要).
在list.Tail当字段属性将抛出一个异常this.tail是null(因为这意味着你得到一个空列表的尾部-这是一个错误),但也有一个内部属性list.TailOrNull,其返回值,而不检查并用来编译模式匹配.
例如,这是一个简单的iter函数:
let rec iter (xs:int list) =
match list with
| x::xs -> Console.WriteLine(x); iter xs
| [] -> ()
Run Code Online (Sandbox Code Playgroud)
这被编译为以下C#代码:
if (list.TailOrNull != null) {
Console.WriteLine(list.HeadOrDefault);
iter(list.TailOrNull);
}
Run Code Online (Sandbox Code Playgroud)
这将检查是否list是通过检查其是否空列表list.TailOrNull是null.如果是,则它知道当前list值是list.Empty表示空列表的值(并且它不打印它,HeadOrDefault因为它假定它也是null如此).
如果您使用defaultof创建null明确的值,那么42::null给你的对象,其中TailOrNull是null不过HeadOrDefault是42.这就是为什么价值不打印的原因.
TL; DR - 不要Unchecked.defaultof与F#类型一起使用.你永远不知道会发生什么.