如何使数组协方差在 F# 中工作

How to make Array Covariance work in F#

来自Array Covariance篇文章:

Arrays where the element type is a reference type are covariant. [...] It was added to the CLR because Java requires it and the CLR designers wanted to be able to support Java-like languages.

CLR 支持数组协变,但如何在 F# 中使用这个方便的功能?

type Foo () = class end
type Bar () = inherit Foo ()

// must have type Foo []
let foo : Foo [] = Array.create 1 (Bar ())
                                // ^^^^^^ must not cast to Foo
// must throw ArrayTypeMismatchException
foo.[0] <- Foo ()

在示例中,我想让 foo 在后台存储一个 Bar 数组,就像在 CLR 中实现的那样。

在许多 C# 允许类型间隐式转换的地方,F# 需要显式转换。

F# 编译器甚至不允许从 Bar[]Foo[] 的直接转换(类型 'Bar []' 没有任何适当的子类型,不能使用作为类型测试或运行时强制转换的来源。),您需要先转换为 object

let foo =  Array.create 1 (Bar ()) :> obj :?> Foo[]

顺便说一句,我不会将数组协方差称为方便的功能,它导致的问题多于解决的问题。

仅作记录,也许数组协方差最有用的最值得注意的情况是当您有一个接受 Foo[] 的函数并且您想使用 Bar[] 参数调用它时。这是 F# 泛型涵盖的内容,因为您可以使用 #Foo[] 语法编写适用于从给定类型继承的任何类型的函数:

type Foo () = class end
type Bar () = inherit Foo ()

let doFooThings (foos:Foo[]) = ()
let doFooThingsBetteer (foos:#Foo[]) = ()

let bars = [| Bar() |]

doFooThings bars         // error: The type 'Foo' does not match the type 'Bar'
doFooThingsBetteer bars  // no error!