Гибкие типы

Заметка гибкого типа указывает, что параметр, переменная или значение имеет тип, совместимый с указанным типом, где совместимость определяется позицией в объектно-ориентированной иерархии классов или интерфейсов. Гибкие типы полезны, особенно если автоматическое преобразование в типы выше в иерархии типов не происходит, но вы по-прежнему хотите включить функциональность для работы с любым типом в иерархии или любым типом, реализующим интерфейс.

Синтаксис

#type

Замечания

В предыдущем синтаксисе тип представляет базовый тип или интерфейс.

Гибкий тип эквивалентен универсальному типу, который имеет ограничение, которое ограничивает допустимые типы типами, совместимыми с базовым или типом интерфейса. То есть следующие две строки кода эквивалентны.

#SomeType

'T when 'T :> SomeType

Гибкие типы полезны в нескольких типах ситуаций. Например, если у вас есть функция с более высоким порядком (функция, которая принимает функцию в качестве аргумента), функция часто полезно возвращать гибкий тип. В следующем примере использование гибкого типа с аргументом iterate2 последовательности позволяет функции более высокого порядка работать с функциями, которые создают последовательности, массивы, списки и любой другой перечислимый тип.

Рассмотрим следующие две функции, одна из которых возвращает последовательность, другая из которых возвращает гибкий тип.

let iterate1 (f : unit -> seq<int>) =
    for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
    for e in f() do printfn "%d" e

// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)

// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])

В качестве другого примера рассмотрим функцию библиотеки Seq.concat :

val concat: sequences:seq<#seq<'T>> -> seq<'T>

В эту функцию можно передать любую из следующих перечисленных последовательностей:

  • Список списков
  • Список массивов
  • Массив списков
  • Массив последовательностей
  • Любое другое сочетание перечисленных последовательностей

Следующий код используется Seq.concat для демонстрации сценариев, которые можно поддерживать с помощью гибких типов.

let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]

let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1

let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]

let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2

let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3

let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4

let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }

let concat5 = Seq.concat [| seq1; seq2; seq3 |]

printfn "%A" concat5

Выходные данные выглядят следующим образом.

seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]

В F#, как и в других объектно-ориентированных языках, существуют контексты, в которых производные типы или типы, реализующие интерфейсы, автоматически преобразуются в базовый тип или тип интерфейса. Эти автоматические преобразования происходят в прямых аргументах, но не в том случае, если тип находится в подчиненном положении, в составе более сложного типа, например возвращаемого типа типа функции или в качестве аргумента типа. Таким образом, нотация гибкого типа в первую очередь полезна при применении типа к нему является частью более сложного типа.

См. также