为什么 Lazy.CreateFromValue 的行为在 .NET Framework 版本之间的 F# 中发生了变化?

Why has the behaviour of Lazy.CreateFromValue changed in F# between .NET framework versions?

我刚刚在 F# 中编译这段代码时遇到了框架版本之间的行为变化:

let test = Lazy.CreateFromValue 1

针对 .NET Framework 2.0 编译,表达式生成 "already created" 惰性对象,即:

test.IsValueCreated = true

针对 .NET Framework 4.0 编译时,表达式会生成 "unevaluated" 惰性对象,即:

 test.IsValueCreated = false

只有在后一种情况下访问test.Value后两者是等价的

我在任何地方都找不到关于此更改的任何参考,因此我的问题是为什么它的行为不同以及更改背后的原因是什么(它正在中断)。在我看来,.NET 2.0 中的行为更有意义 - 从具体值创建惰性对象应该导致 "already evaluated" 惰性对象。

Lazy<T> class 已添加到 .NET 4 中的框架。F# 对惰性的支持更像是一个允许 API 在 .NET 2 上工作的存根, 但在使用 API.

时不提供真正的惰性实现

F# 编译器在使用 CreateFromValue.

时创建了 own lazy type for .NET 2, as it predated the .NET 4 version. In .NET 2, the lazy type just sets the value explicitly, and isn't truly lazy (see the source)

因此,CreateFromValue extension on .NET 4 is truly lazy - it instantiates the lazy type with a function that returns the value. On .NET 2, the internal type is used instead 满足了 Lazy<'T>.CreateFromValue 的 API,但并不是真正的懒惰。

在 .NET 4.0 之前,框架未随 Lazy<'T> 一起提供。

那是古老的历史,但 F# 最初有自己的 Lazy 实现,与我们今天所拥有的不同 - 您可以瞥见它 here

当很明显 Lazy 即将进入框架时,该实施被放弃了。相反,F# 2.0 在 FSharp.Core 程序集中附带了自己的 System.Lazy<'T>(注意命名空间)实现。这是您仍然可以看到的实现 here。这个想法是,一旦 .NET 4.0 可用,F# 将从那里无缝地获取 System.Lazy<'T>,而不会影响用户。这在大多数情况下都有效,但并非完全没有问题。

您会注意到,F# 实现有一个 CreateFromValue 成员,它产生一个类型为 Lazy<'T> 的值,该值已标记为已评估。这在语义上非常有意义,因为您显然首先给它一个评估值。

为什么 .NET 4.0 实现不做同样的事情?

如果你用 F# 查看 documentation for Lazy<'T>, you'll find that there's no way of creating it in an evaluated state. There's no CreateFromValue member on it, and no constructor takes a value of 'T, only a Func<'T>. CreateFromValue is actually provided as an extension method

以不间断的方式提供此方法将非常容易:

static member CreateFromValue(value : 'T) : System.Lazy<'T> =
    let x = System.Lazy<'T>.Create(fun () -> value)
    x.Value |> ignore
    x

但出于某种原因,这并没有发生。也许这是一个深思熟虑的选择——我想你可以为这种改变争论不休——但也许这是一个疏忽。如果你看看这种类型的错综复杂的历史,我想你会同意它可能会更糟。