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


Приведение и преобразование (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, или из float32float64, или когда исходный или целевой тип неизвестен.

Однако неявное расширение допускается для 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 , в том, чтобы nativeintint64, int32 и int32doubleв случае, когда исходный и целевой тип известны во время вывода типов. Поэтому в таких случаях, как предыдущие примеры, 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 при аргументах метода)

См. также