Я пытаюсь вызвать метод .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 '[email protected][System.Int32]'
// to type 'System.Collections.Generic.IEnumerable`1[System.Object]'.
В идеале я хотел бы, чтобы первый синтаксис работал - или как можно ближе к нему, с проверкой типа времени компиляции. Я не понимаю, где компилятор находит функцию seq<string> -> unit
в этой строке, но, по-видимому, ковариация для IEnumerable не работает и что-то приводит к этому сообщению об ошибке. Использование явного результата приведения в разумное сообщение об ошибке - но оно тоже не работает. Использование выполнения выполнения во время выполнения - но только для строк, ints fail с исключением (противный).
Я пытаюсь взаимодействовать с другим кодом .NET; поэтому мне нужны конкретные типы IEnumerable.
Какой самый чистый и, возможно, эффективный способ литья ко- или контравариантных интерфейсов, таких как IEnumerable в F #?