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"
These are the results I get:
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]'.
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).
I'm trying to interoperate with other .NET code; that's why I need specific IEnumerable types.
What's the cleanest and preferably efficient way of casting co- or contravariant interfaces such as IEnumerable in F#?
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…