Приведение и преобразование (F#)
В этой статье описывается поддержка преобразований типов в F#.
Арифметические типы
F# предоставляет операторы преобразования для арифметических преобразований между различными примитивными типами, например между целыми числами и типами с плавающей запятой. Операторы преобразования целочисленных и char имеют проверка и не проверка форм; операторы с плавающей enum
запятой и оператор преобразования не выполняются. Формы без проверка определяются и FSharp.Core.Operators
в проверка формах определяются в FSharp.Core.Operators.Checked
. Формы проверка проверка для переполнения и создают исключение среды выполнения, если результирующее значение превышает пределы целевого типа.
Каждый из этих операторов имеет то же имя, что и имя типа назначения. Например, в следующем коде, в котором типы явно аннотированы, byte
отображаются с двумя разными значениями. Первое вхождение — тип, а второй — оператор преобразования.
let x : int = 5
let b : byte = byte x
В следующей таблице показаны операторы преобразования, определенные в F#.
Operator | Description |
---|---|
byte |
Преобразуйте в байт 8-разрядный тип без знака. |
sbyte |
Преобразование в подписанный байт. |
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 cannot be determined by type inference, so the type parameter must be explicit.
let col1 = enum<Color> 1
// The target type is supplied by a type annotation.
let col2 : Color = enum 2
Можно также явно указать тип перечисления целевого объекта в качестве параметра типа, как показано в следующем коде:
let col3 = enum<Color> 3
Обратите внимание, что приведение перечисления работает только в том случае, если базовый тип перечисления совместим с преобразованным типом. В следующем коде преобразование не может компилироваться из-за несоответствия между int32
и uint32
.
// Error: types are incompatible
let col4 : Color = enum 2u
Дополнительные сведения см. в разделе "Перечисления".
Приведение типов объектов
Преобразование между типами в иерархии объектов является фундаментальным для объектно-ориентированного программирования. Существует два основных типа преобразований: приведение вверх (переадресовка) и приведение вниз (вниз). Приведение иерархии означает приведение из производной ссылки на объект в ссылку на базовый объект. Такой приведение гарантированно работает до тех пор, пока базовый класс находится в иерархии наследования производного класса. Приведение к иерархии из ссылки базового объекта на производную ссылку на объект выполняется успешно, только если объект фактически является экземпляром правильного типа назначения (производного) или типа, производного от типа назначения.
F# предоставляет операторы для этих типов преобразований. Оператор :>
вызывает иерархию, а :?>
оператор приводит к низу иерархии.
Upcasting
Во многих объектно-ориентированных языках переадресация неявна; в F#правила немного отличаются. Переадресация применяется автоматически при передаче аргументов методам типа объекта. Однако для функций с привязкой let-bound в модуле upcasting не является автоматическим, если только тип параметра не объявлен как гибкий тип. Дополнительные сведения см. в разделе "Гибкие типы".
Оператор :>
выполняет статический приведение, что означает, что успешность приведения определяется во время компиляции. Если приведение, использующее :>
компиляцию успешно, это допустимый приведение и не имеет шансов на сбой во время выполнения.
Оператор также можно использовать upcast
для выполнения такого преобразования. Следующее выражение указывает преобразование в иерархию:
upcast expression
При использовании оператора upcast компилятор пытается определить тип, который вы преобразуете из контекста. Если компилятор не может определить целевой тип, компилятор сообщает об ошибке. Может потребоваться заметка типа.
Даунинг
Оператор :?>
выполняет динамический приведение, что означает, что успешность приведения определяется во время выполнения. Приведение, использующее :?>
оператор, не проверка во время компиляции; но во время выполнения предпринимается попытка приведения к указанному типу. Если объект совместим с целевым типом, приведение завершается успешно. Если объект несовместим с целевым типом, среда выполнения вызывает объект InvalidCastException
.
Оператор также можно использовать downcast
для выполнения динамического преобразования типов. Следующее выражение указывает преобразование вниз иерархии в тип, который выводится из контекста программы:
downcast expression
Что касается upcast
оператора, то если компилятор не может определить конкретный целевой тип из контекста, он сообщает об ошибке. Может потребоваться заметка типа.
Следующий код иллюстрирует использование :>
операторов и :?>
операторов. В коде показано, что оператор лучше всего используется, если вы знаете, что :?>
преобразование будет выполнено успешно, так как оно вызывает 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
в предыдущем примере let base1: Base1 = upcast d1
кода на .
Требуется заметка типа, так как upcast
сама по себе не могла определить базовый класс.
Неявные преобразования вверх
Неявные передачи вставляются в следующие ситуации:
При предоставлении параметра функции или метода с известным именованным типом. Это включает в себя, когда конструкция, например вычисления выражений или срезов, становится вызовом метода.
При назначении или изменении поля записи или свойства с известным именованным типом.
Если ветвь
if/then/else
илиmatch
выражение имеет известный целевой тип, возникающий из другой ветви или общего известного типа.Если элемент списка, массива или выражения последовательности имеет известный целевой тип.
Рассмотрим следующий пример кода:
open System
open System.IO
let findInputSource () : TextReader =
if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
// On Monday a TextReader
Console.In
else
// On other days a StreamReader
File.OpenText("path.txt")
Здесь ветви условного вычисления TextReader
и StreamReader
соответственно. Во второй ветви известный целевой тип находится TextReader
из заметки типа метода и из первой ветви. Это означает, что вторая ветвь не требуется переадресовка.
Чтобы отобразить предупреждение на каждой точке, используется дополнительная неявная рассылка, можно включить предупреждение 3388 (/warnon:3388
или свойство <WarnOn>3388</WarnOn>
).
Неявные числовые преобразования
F# использует явное расширение числовых типов в большинстве случаев с помощью операторов преобразования. Например, явное расширение требуется для большинства числовых типов, таких как int8
или int16
, или из float32
float64
, или когда исходный или целевой тип неизвестен.
Однако неявное расширение допускается для 32-разрядных целых чисел, расширяемых до 64-разрядных целых чисел, в тех же ситуациях, что и неявные переадресации. Например, рассмотрим типичную форму API:
type Tensor(…) =
static member Create(sizes: seq<int64>) = Tensor(…)
Можно использовать целые литералы для int64:
Tensor.Create([100L; 10L; 10L])
Или целые литералы для int32:
Tensor.Create([int64 100; int64 10; int64 10])
Расширение происходит автоматически для int32
, в том, чтобы nativeint
int64
, int32
и int32
double
в случае, когда исходный и целевой тип известны во время вывода типов. Поэтому в таких случаях, как предыдущие примеры, int32
можно использовать литералы:
Tensor.Create([100; 10; 10])
Кроме того, можно включить предупреждение 3389 (/warnon:3389
или свойство <WarnOn>3389</WarnOn>
) для отображения предупреждения в каждой точке неявного числового расширения.
. Неявные преобразования в стиле NET
API .NET позволяют определению статических op_Implicit
методов предоставлять неявные преобразования между типами. Они применяются автоматически в коде F# при передаче аргументов в методы. Например, рассмотрим следующий код, выполняя явные вызовы op_Implicit
методов:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")
. Преобразования стилей op_Implicit
NET применяются автоматически для выражений аргументов, когда типы доступны для исходного выражения и целевого типа:
open System.Xml.Linq
let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")
Вы также можете включить предупреждение 3395 (/warnon:3395
или свойство <WarnOn>3395</WarnOn>
), чтобы отобразить предупреждение в каждой точке a. Используется неявное преобразование в стиле NET.
. Преобразования в стиле op_Implicit
NET также применяются автоматически для выражений, отличных от метода, в тех же ситуациях, что и неявные переадресации. Однако при использовании широко или неуместно неявные преобразования могут плохо взаимодействовать с выводом типов и привести к коду, который труднее понять. По этой причине они всегда создают предупреждения при использовании в позициях, отличных от аргументов.
Чтобы отобразить предупреждение в каждом моменте, что a . Неявное преобразование в стиле NET используется для аргумента, отличного от метода, можно включить предупреждение 3391 (/warnon:3391
или свойство <WarnOn>3391</WarnOn>
).
Сводка предупреждений, связанных с преобразованиями
Для использования неявных преобразований предоставляются следующие необязательные предупреждения:
/warnon:3388
(дополнительная неявная рассылка)/warnon:3389
(неявное расширение числовых элементов)/warnon:3391
(op_Implicit
при аргументах, отличных от метода, по умолчанию)/warnon:3395
(op_Implicit
при аргументах метода)