Я пытаюсь вызвать метод .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 не работает, и это как-то приводит к этому сообщению об ошибке. Использование явного приведения приводит к разумному сообщению об ошибке - но оно также не работает. Использование приведений во время выполнения - но только для строк, целые числа терпят неудачу с исключением (неприятно).
Я пытаюсь взаимодействовать с другим кодом .NET; Вот почему мне нужны определенные типы IEnumerable.
Какой самый чистый и желательно эффективный способ приведения ко-или контравариантных интерфейсов, таких как IEnumerable в F #?