Поделиться через


Обобщенные понятия

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

Синтаксис

// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body

// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body

// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition

Замечания

Объявление явно универсальной функции или типа очень похоже на не универсальную функцию или тип, за исключением спецификации (и использования) параметров типа в угловых скобках после имени функции или типа.

Объявления часто неявно универсальны. Если вы не полностью укажете тип каждого параметра, используемого для создания функции или типа, компилятор пытается определить тип каждого параметра, значения и переменной из написанного кода. Дополнительные сведения см. в разделе "Вывод типов". Если код типа или функции не ограничивает типы параметров, функция или тип неявно универсальны. Этот процесс называется автоматической обобщением. Существуют некоторые ограничения на автоматическую обобщение. Например, если компилятор F# не может выводить типы для универсальной конструкции, компилятор сообщает об ошибке, которая относится к ограничению, называемому ограничением значения. В этом случае может потребоваться добавить некоторые заметки типа. Дополнительные сведения о автоматической обобщении и ограничении значений, а также о том, как изменить код для решения проблемы, см. в статье "Автоматическое обобщение".

В предыдущем синтаксисе тип-параметры — это разделенный запятыми список параметров, представляющих неизвестные типы, каждый из которых начинается с одной кавычки, при необходимости с предложением ограничения, которое дополнительно ограничивает типы, которые могут использоваться для этого параметра типа. Синтаксис предложений ограничений различных типов и других сведений об ограничениях см. в разделе "Ограничения".

Определение типа в синтаксисе совпадает с определением типа для не универсального типа. Он включает параметры конструктора для типа класса, необязательное as предложение, равный символ, поля записи, предложение, inherit варианты для различаемого объединения let и привязки, определения членов и do все остальное, разрешенное в определении не универсального типа.

Другие элементы синтаксиса совпадают с элементами, не являющиеся универсальными функциями и типами. Например, идентификатор объекта — это идентификатор, представляющий сам содержащий объект.

Свойства, поля и конструкторы не могут быть более универсальными, чем вложенный тип. Кроме того, значения в модуле не могут быть универсальными.

Неявно универсальные конструкции

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

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

let makeList a b = [ a; b ]

Сигнатура функции выводится.'a -> 'a -> 'a list Обратите внимание, что a и b в этом примере выводятся для того же типа. Это связано с тем, что они включены в список вместе, и все элементы списка должны иметь одинаковый тип.

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

let function1 (x: 'a) (y: 'a) = printfn "%A %A" x y

Явные универсальные конструкции

Вы также можете сделать функцию универсальной, явно объявляя его параметры типа в угловых скобках (<type-parameter>). Приведенный ниже код иллюстрирует это.

let function2<'T> (x: 'T) (y: 'T) = printfn "%A, %A" x y

Использование универсальных конструкций

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

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

// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
    // The compiler reports a warning:
    function1<int> a b
    // No warning.
    function2<int> a b

Замечание

Существует два способа ссылки на универсальный тип по имени. Например, list<int> и int list два способа ссылаться на универсальный тип list , имеющий один аргумент intтипа. Последняя форма обычно используется только со встроенными типами F#, такими как list и option. При наличии нескольких аргументов типа обычно используется синтаксис Dictionary<int, string> , но можно также использовать синтаксис (int, string) Dictionary.

Подстановочные знаки в качестве аргументов типа

Чтобы указать, что аргумент типа должен быть выведен компилятором, можно использовать символ подчеркивания или подстановочный знак (_), а не именованный аргумент типа. Это показано в следующем коде.

let printSequence (sequence1: Collections.seq<_>) =
    Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1

Ограничения в универсальных типах и функциях

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

Статически разрешенные параметры типа

Существует два типа параметров типа, которые можно использовать в программах F#. Первое — это параметры универсального типа типа, описанного в предыдущих разделах. Этот первый тип параметра типа эквивалентен параметрам универсального типа, используемым на таких языках, как Visual Basic и C#. Другой тип параметра типа относится к F# и называется статически разрешенным параметром типа. Дополнительные сведения об этих конструкциях см. в разделе "Параметры типа статического разрешения".

Примеры

// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x: 'a) (y: 'a) = printf "%A %A" x y

// A generic record, with the type parameter in angle brackets.
type GR<'a> = { Field1: 'a; Field2: 'a }

// A generic class.
type C<'a>(a: 'a, b: 'a) =
    let z = a
    let y = b
    member this.GenericMethod(x: 'a) = printfn "%A %A %A" x y z

// A generic discriminated union.
type U<'a> =
    | Choice1 of 'a
    | Choice2 of 'a * 'a

type Test() =
    // A generic member
    member this.Function1<'a>(x, y) = printfn "%A, %A" x y

    // A generic abstract method.
    abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
    override this.abstractMethod<'a, 'b>(x: 'a, y: 'b) = printfn "%A, %A" x y

См. также