Приведение и преобразование (F#)
В этом разделе описывается поддержка преобразований типов в языке F#.
Арифметические типы
В языке F# имеются операторы преобразования между различными типами-примитивами, например между целыми числами и числами с плавающей запятой.Операторы интегрирования и преобразования символов имеют проверяемые и непроверяемые формы, в отличие от операторов для чисел с плавающей запятой и операторов преобразования enum.Непроверяемые формы определены в пространстве имен Microsoft.FSharp.Core.Operators, а проверяемые — в пространстве имен Microsoft.FSharp.Core.Operators.Checked.В проверяемых формах выполняется проверяется на переполнение, и если обнаруживается, что результирующее значение превышает допустимый диапазон целевого типа, выдается исключение в среде выполнения.
Имя каждого из этих операторов совпадает с именем целевого типа.Например, в следующем коде с явно указанными типами слово byte имеет два значения.В первом случае оно обозначает тип, во втором — оператор преобразования.
let x : int = 5
let b : byte = byte x
В следующей таблице показаны операторы преобразования, определенные в языке F#.
Оператор |
Описание |
---|---|
byte |
Преобразование в тип byte, 8-разрядное число без знака. |
sbyte |
Преобразование в 8-разрядное число со знаком. |
int16 |
Преобразование в 16-разрядное целое число со знаком. |
uint16 |
Преобразование в 16-разрядное целое число без знака. |
int32, int |
Преобразование в 32-разрядное целое число со знаком. |
uint32 |
Преобразование в 32-разрядное целое число без знака. |
int64 |
Преобразование в 64-разрядное целое число со знаком. |
uint64 |
Преобразование в 64-разрядное целое число без знака. |
nativeint |
Преобразование в целое число, разрядность которого соответствует разрядности системы. |
unativeint |
Преобразование в целое число без знака, разрядность которого соответствует разрядности системы. |
float, double |
Преобразование в 64-разрядное число с плавающей точкой двойной точности (стандарт IEEE). |
float32, single |
Преобразование в 32-разрядное число с плавающей точкой одиночной точности (стандарт IEEE). |
decimal |
Преобразование в тип System.Decimal. |
char |
Преобразование в тип System.Char (символ Юникода). |
enum |
Преобразование в перечисляемый тип. |
Помимо встроенных примитивных типов, эти операторы можно использовать с типами, реализующими методы op_Explicit или op_Implicit с соответствующими сигнатурами.Например, оператор преобразования int работает с любым типом, предоставляющим статический метод op_Explicit, который принимает этот тип в качестве параметра и возвращает тип int.В качестве особого исключения из общего правила о том, что методы не могут быть перегружены по возвращаемому типу, для операторов op_Explicit и op_Implicit такая перегрузка возможна.
Типы перечислений
enum — универсальный оператор, принимающий один параметр. Этот параметр представляет тип enum, преобразование в который требуется выполнить.При преобразовании в перечисляемый тип алгоритм определения типа пытается определить, в какой тип enum требуется выполнить преобразование.В следующем примере тип переменной col1 не указан явно, но в дальнейшем он определяется по проверке равенства.Таким образом, компилятор определяет, что требуется выполнить преобразование в перечисление типа Color.Вместо этого можно указать тип явно, как это сделано в следующем примере (в нем указан тип col2).
type Color =
| Red = 1
| Green = 2
| Blue = 3
// The target type of the conversion is determined by type inference.
let col1 = enum 1
// The target type is supplied by a type annotation.
let col2 : Color = enum 2
do
if (col1 = Color.Red) then
printfn "Red"
Также можно указать тип перечисления целевого объекта явно в качестве параметра типа, как показано в следующем коде:
let col3 = enum<Color> 3
Обратите внимание, что приведения перечисления работают, только если базовый тип перечисления, который совместим с преобразования типа.В следующем коде преобразование не может компилировать из-за несоответствия между int32 и uint32.
// Error: types are incompatible
let col4 : Color = enum 2u
Дополнительные сведения см. в разделе Перечисления (F#).
Приведение типов объектов
Преобразование между различными типами в иерархии объектов — один из важнейших аспектов объектно-ориентированного программирования.Существует два основных типа преобразования: приведение с повышением и приведение с понижением.Приведение с повышением по иерархии означает приведение объекта производного типа к его базовому типу.Такое приведение всегда выполняется успешно, если базовый класс принадлежит к иерархии наследования производного класса.Приведение с понижением по иерархии (от базового типа объекта к производному) возможно только в том случае, если объект является экземпляром целевого (производного) типа или типа, производного от целевого.
В языке F# имеются операторы для этих типов преобразования.Оператор :> обеспечивает приведение с повышением по иерархии, а оператор :?> — приведение с понижением по иерархии.
Приведение с повышением по иерархии
Во многих объектно-ориентированных языках приведение с повышением по иерархии задается неявно. В F# правила несколько отличаются.Приведение с повышением по иерархии выполняется автоматически, когда методу типа объекта передаются аргументы.Впрочем, для функций модуля, созданных с помощью привязки let, приведение с повышением по иерархии не выполняется автоматически, если тип параметра не объявлен как гибкий тип.Дополнительные сведения см. в разделе Гибкие типы (F#).
Оператор :> выполняет статическое приведение, т. е. успешность приведения определяется во время компиляции.Если приведение с помощью оператора :> успешно обработано при компиляции, оно является действительным и не может вызвать ошибку во время выполнения.
Это преобразование также можно выполнить с помощью оператора upcast.Следующее выражение обеспечивает преобразование с повышением по иерархии.
upcast expression
При использовании оператора upcast компилятор пытается определить тип, в который требуется выполнить преобразование, по контексту.Если компилятору не удается определить целевой тип, он выдает ошибку.
Приведение с понижением по иерархии
Оператор :?> выполняет динамическое приведение, т. е. успешность приведения определяется во время выполнения.Приведение с помощью оператора :?> не проверяется во время компиляции. Во время выполнения производится попытка приведения к указанному типу.Если объект совместим с целевым типом, приведение выполняется успешно.В противном случае во время выполнения выдается исключение InvalidCastException.
Динамическое преобразование типа также можно выполнить с помощью оператора downcast.В следующем выражении задается преобразование с понижением по иерархии к типу, определяемому по контексту программы.
downcast expression
Тогда как при использовании оператора приведения с повышением по иерархии если компилятору не удается определить целевой тип по контексту, выдается ошибка.
В следующем примере кода демонстрируется использование операторов :> и :?>.В нем показано, что оператор :?> следует использовать, если точно известно, что преобразование будет выполнено успешно, поскольку в противном случае будет выдано исключение InvalidCastException.Если неизвестно, будет ли преобразование успешным, выгоднее использовать выражение match, поскольку оно не приводит к риску создания исключения.
type Base1() =
abstract member F : unit -> unit
default u.F() =
printfn "F Base1"
type Derived1() =
inherit Base1()
override u.F() =
printfn "F Derived1"
let d1 : Derived1 = Derived1()
// Upcast to Base1.
let base1 = d1 :> Base1
// This might throw an exception, unless
// you are sure that base1 is really a Derived1 object, as
// is the case here.
let derived1 = base1 :?> Derived1
// If you cannot be sure that b1 is a Derived1 object,
// use a type test, as follows:
let downcastBase1 (b1 : Base1) =
match b1 with
| :? Derived1 as derived1 -> derived1.F()
| _ -> ()
downcastBase1 base1
Поскольку универсальные операторы downcast и upcast используют алгоритмы определения типа для определения типов аргумента и возвращаемого объекта, в приведенном выше коде можно заменить выражение
let base1 = d1 :> Base1
на
base1 = upcast d1
В приведенном выше коде тип аргумента — Derived1, а тип возвращаемого объекта — Base1.
Дополнительные сведения о тестировании типов см. в разделе Выражения match (F#).