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


Приведение и преобразование (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"

Приведение типов объектов

Преобразование между различными типами в иерархии объектов — один из важнейших аспектов объектно-ориентированного программирования. Существует два основных типа преобразования: приведение с повышением и приведение с понижением. Приведение с повышением по иерархии означает приведение объекта производного типа к его базовому типу. Такое приведение всегда выполняется успешно, если базовый класс принадлежит к иерархии наследования производного класса. Приведение с понижением по иерархии (от базового типа объекта к производному) возможно только в том случае, если объект является экземпляром целевого (производного) типа или типа, производного от целевого.

В языке 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#).

См. также

Другие ресурсы

Справочник по языку F#

Журнал изменений

Дата

Журнал

Причина

Сентябрь 2010

Добавлены сведения о перегрузке операторов op_Explicit и op_Implicit по возвращаемому типу.

Улучшение информации.