Eam*_*nne 12 ienumerable f# casting covariance
我试图调用一个.NET方法接受IEnumerable<T>
来自F#的泛型,使用seq<U>
U是T的子类.这不会像我预期的那样工作:
使用以下简单的打印机:
let printEm (os: seq<obj>) =
for o in os do
o.ToString() |> printfn "%s"
Run Code Online (Sandbox Code Playgroud)
这些是我得到的结果:
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]'.
Run Code Online (Sandbox Code Playgroud)
理想情况下,我希望第一种语法可以工作 - 或者使用尽可能接近它的东西,使用编译时类型检查.我不明白编译器seq<string> -> unit
在该行中找到函数的位置,但显然IEnumerable的协方差不起作用,并以某种方式导致该错误消息.使用显式强制转换会产生合理的错误消息 - 但它也不起作用.使用运行时强制转换可以工作 - 但仅限于字符串,int会因异常(令人讨厌)而失败.
我正在尝试与其他.NET代码进行互操作; 这就是为什么我需要特定的IEnumerable类型.
什么是最简洁,最有效的方法来构建共同或逆变接口,例如F#中的IEnumerable?
des*_*sco 10
不幸的是,F#并不支持共同逆转.这就是为什么这个
Seq.singleton "Hello World" :> seq<obj> |> printEm
Run Code Online (Sandbox Code Playgroud)
不起作用
您可以将参数声明为seq <_>,或者通过使用灵活类型 s(使用hash#)将参数类型集限制为某个特定系列,这将修复此方案:
let printEm (os: seq<_>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton "Hello World" |> printEm
Run Code Online (Sandbox Code Playgroud)
考虑到这条线:
Seq.singleton 42 :> seq<obj> |> printEm // error FS0193
Seq.singleton 42 :?> seq<obj> |> printEm
Run Code Online (Sandbox Code Playgroud)
方差仅适用于类,因此类似的代码也不适用于C#.
您可以尝试通过Seq.cast将序列元素转换为所需的类型明确性
使用Seq.cast
此.例如:
Seq.singleton "Hello World" |> Seq.cast |> printEm
Run Code Online (Sandbox Code Playgroud)
不可否认,这放弃了类型安全:
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!
Run Code Online (Sandbox Code Playgroud)
但这是权宜之计.
或者,您可以使用灵活类型:
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
Run Code Online (Sandbox Code Playgroud)
这只是通用"forall类型'a when 'a :> Dog
"的简写.
最后,您可以始终映射上传,例如
let printEm (os: seq<obj>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton "Hello" |> Seq.map box |> printEm // ok
Run Code Online (Sandbox Code Playgroud)
在哪里box
向上倾斜obj
.