F# 和接口(interface)协方差 : what to do?(特别是 seq<> aka IEnumerable<>)

标签 f# casting ienumerable covariance

我正在尝试调用一个接受通用 IEnumerable<T> 的 .NET 方法从 F# 使用 seq<U>这样 U 是 T 的子类。这不像我预期的那样工作:

使用以下简单的打印机:

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> -> unit该行中的函数,但显然 IEnumerable 的协方差不起作用,并且以某种方式导致该错误消息。使用显式强制转换会导致合理的错误消息 - 但它也不起作用。使用运行时强制转换有效 - 但仅适用于字符串,整数因异常而失败(讨厌)。

我正在尝试与其他 .NET 代码互操作;这就是为什么我需要特定的 IEnumerable 类型。

在 F# 中转换协变或逆变接口(interface)(例如 IEnumerable)的最干净且最有效的方法是什么?

最佳答案

使用 Seq.cast 为了这。例如:

Seq.singleton "Hello World"  |> Seq.cast |> printEm

诚然,这放弃了类型安全:
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!

但这是权宜之计。

或者,您可以使用 flexible types :
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”的简写。

最后,您始终可以映射上播,例如
let printEm (os: seq<obj>) =  
    for o in os do 
        o.ToString() |> printfn "%s" 

Seq.singleton "Hello" |> Seq.map box |> printEm // ok

在哪里 box向上转换为 obj .

关于F# 和接口(interface)协方差 : what to do?(特别是 seq<> aka IEnumerable<>),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4203560/

相关文章:

wpf - 为什么程序退出时不触发 OnSessionEnding 事件?

f# - 异步数据库查询

c# - IQueryable 和 IEnumerable 之间区别的真实示例?

c# - yield return 仅适用于 IEnumerable<T>?

.net-core - 起订量:如何在没有占位符接口(interface)的情况下模拟 F# 中的函数?

logging - 如何在 F# Fable 中登录 Application Insights?

C:在不强制转换的情况下对无符号变量执行有符号比较

java - 避免在继承的 java 类中进行强制转换

arrays - 将对象数组转换为数组数组

c# - IEnumerable<char> 到字符串