F#和接口协方差:该怎么办? (具体地,可以是IEnumerable<>) [英] F# and interface covariance: what to do? (specifically seq<> aka IEnumerable<>)

查看:129
本文介绍了F#和接口协方差:该怎么办? (具体地,可以是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&lt;&gt;)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆