F#和接口协方差:该怎么办? (具体地,可以是IEnumerable<>) [英] F# and interface covariance: what to do? (specifically seq<> aka IEnumerable<>)
问题描述
我试图调用一个.NET方法接受来自F#使用 seq< U> $的通用
IEnumerable< T>
c $ c>使用下面的简单打印机:
I'm trying to call a .NET method accepting a generic IEnumerable<T>
from F# using a seq<U>
such that U is a subclass of T. This doesn't work the way I expected it would:
With the following simple printer:
let printEm (os: seq<obj>) =
for o in os do
o.ToString() |> printfn "%s"
这些是我得到的结果:
Seq.singleton "Hello World" |> printEm // error FS0001;
//Expected seq<string> -> 'a but given seq<string> -> unit
Seq.singleton "Hello World" :> seq<obj> |> printEm // error FS0193;
//seq<string> incompatible with seq<obj>
Seq.singleton "Hello World" :?> seq<obj> |> printEm // works!
Seq.singleton 42 :> seq<obj> |> printEm // error FS0193
Seq.singleton 42 :?> seq<obj> |> printEm // runtime InvalidCastException!
//Unable to cast object of type 'mkSeq@541[System.Int32]'
// to type 'System.Collections.Generic.IEnumerable`1[System.Object]'.
理想情况下,我希望第一个语法可以工作,与编译时类型检查。我不明白编译器在哪里找到 seq< string> - >单位
函数,但显然协议的IEnumerable不工作,并以某种方式导致该错误消息。使用显式转换会产生合理的错误消息 - 但它也不起作用。使用运行时转换工作 - 但只对字符串,整数失败与异常(讨厌)。
Ideally, I'd like the first syntax to work - or something as close to it as possible, with compile time type checking. I don't understand where the compiler's finding a seq<string> -> unit
function in that line, but apparently covariance for IEnumerable isn't working and that somehow results in that error message. Using an explicit cast results in a reasonable error message - but it doesn't work either. Using a runtime cast works - but only for strings, ints fail with an exception (nasty).
我试图与其他.NET代码互操作;这是为什么我需要特定的IEnumerable类型。
I'm trying to interoperate with other .NET code; that's why I need specific IEnumerable types.
什么是最干净,最好是有效的方式来投射协同或逆向接口,如F#中的IEnumerable?
What's the cleanest and preferably efficient way of casting co- or contravariant interfaces such as IEnumerable in F#?
推荐答案
使用 Seq.cast
。例如:
Use Seq.cast
for this. For example:
Seq.singleton "Hello World" |> Seq.cast |> printEm
诚然,这放弃了类型安全:
Admittedly, this gives up type safety:
type Animal() = class end
type Dog() = inherit Animal()
type Beagle() = inherit Dog()
let printEm (os: seq<Dog>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton (Beagle()) |> Seq.cast |> printEm // ok
Seq.singleton (Animal()) |> Seq.cast |> printEm // kaboom!
但它是有利的。
或者,您也可以使用灵活类型:
type Animal() = class end
type Dog() = inherit Animal()
type Beagle() = inherit Dog()
let printEm (os: seq<#Dog>) = // note #Dog
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton (Beagle()) |> printEm // ok
Seq.singleton (Animal()) |> printEm // type error
这是通用的forall types 'a when'a:> Dog
。
which is just shorthand for the generic "forall types 'a when 'a :> Dog
".
最后,您可以随时映射上传,例如
And finally, you can always map the upcast, e.g.
let printEm (os: seq<obj>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton "Hello" |> Seq.map box |> printEm // ok
其中盒
obj
。
这篇关于F#和接口协方差:该怎么办? (具体地,可以是IEnumerable<>)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!