代码之家  ›  专栏  ›  技术社区  ›  Eamon Nerbonne

F和接口协方差:怎么办?(具体来说,seq<>aka ienumerable<>)

  •  12
  • Eamon Nerbonne  · 技术社区  · 14 年前

    我正在尝试调用接受泛型的.NET方法 IEnumerable<T> 从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的协方差不起作用,这会导致错误消息。使用显式强制转换会产生合理的错误消息——但也不起作用。使用运行时强制转换是可行的,但只适用于字符串,ints会因异常(令人讨厌)而失败。

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

    什么是最干净和最好有效的方法来铸造共变量或反变量接口,如F中的IEnumerable?

    2 回复  |  直到 14 年前
        1
  •  8
  •   Brian    14 年前

    使用 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
    

    这只是一般“所有类型”的简写 '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 .

        2
  •  10
  •   desco    14 年前

    不幸的是,F_不支持协变。这就是为什么

    Seq.singleton "Hello World"  :> seq<obj> |> printEm 
    

    不起作用

    您可以将参数声明为seq,或者使用 flexible type S (使用hash)这将修复此方案:

    let printEm (os: seq<_>) = 
    for o in os do
        o.ToString() |> printfn "%s"
    
    Seq.singleton "Hello World"  |> printEm 
    

    考虑到这一行:

    Seq.singleton 42 :> seq<obj> |> printEm // error FS0193
    Seq.singleton 42 :?> seq<obj> |> printEm
    

    差异只适用于类,因此类似的代码在C中也不适用。

    您可以尝试通过seq.cast将序列元素强制转换为所需的类型。