キャストと変換 (F#)

この記事では、F# での型変換のサポートについて説明します。

算術型

F# では、整数型と浮動小数点型の間など、さまざまなプリミティブ型間の算術変換のための変換演算子が用意されています。 整数および文字の変換演算子には、checked と unchecked の形式があります。浮動小数点演算子と enum 変換演算子にはありません。 unchecked 形式は FSharp.Core.Operators で定義され、checked 形式は FSharp.Core.Operators.Checked で定義されます。 checked 形式では、オーバーフローがチェックされ、結果の値が対象の型の制限を超えた場合、ランタイム例外が生成されます。

これらの各演算子には、変換先の型の名前と同じ名前が付けられます。 たとえば、次のコードでは、型に明示的に注釈が付けられており、"byte" は 2 つの異なる意味で記述されています。 最初の出現は型で、2 番目は変換演算子です。

let x : int = 5

let b : byte = byte x

次の表に、F# で定義されている変換演算子を示します。

演算子 説明
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 Unicode 文字である、System.Char に変換します。
enum 列挙型に変換します。

これらの演算子は、組み込みのプリミティブ型に加えて、適切なシグネチャを持つ、op_Explicit または op_Implicit メソッドを実装する型で使用できます。 たとえば、int 変換演算子は、型をパラメーターとして受け取り、int を返す静的メソッド op_Explicit を提供する任意の型で動作します。 メソッドを戻り値の型によってオーバーロードすることはできないという、一般的な規則の特別な例外として、op_Explicit および op_Implicit に対してこれを行うことができます。

列挙型

enum 演算子は、変換先の enum の型を表す 1 つの型パラメーターを受け取る汎用演算子です。 列挙型に変換すると、型の推定によって、変換先の 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

列挙キャストは、列挙型の基になる型が変換される型と互換性がある場合にのみ機能します。 次のコードでは、int32uint32 の間に不一致があるため、変換はコンパイルされません。

// Error: types are incompatible
let col4 : Color = enum 2u

詳細については、「列挙型」を参照してください。

オブジェクト型のキャスト

オブジェクト階層内の型間の変換は、オブジェクト指向プログラミングの基本となります。 変換には、上位へのキャスト (アップキャスト) と下位へのキャスト (ダウンキャスト) の 2 つの基本的なタイプがあります。 階層の上位へのキャストは、派生オブジェクト参照からの基底オブジェクト参照へのキャストを意味します。 このようなキャストは、基底クラスが派生クラスの継承階層内にある限り、確実に動作します。 基底オブジェクト参照から派生オブジェクト参照への階層の下位へのキャストは、オブジェクトが実際に正しい宛先 (派生) 型のインスタンスであるか、または宛先の型から派生した型である場合にのみ成功します。

F# では、これらの種類の変換のための演算子が用意されています。 :> 演算子は階層を上位方向にキャストし、:?> 演算子は階層を下位方向にキャストします。

アップキャスト

多くのオブジェクト指向言語では、アップキャストは暗黙的です。F# では、ルールが若干異なります。 アップキャストは、オブジェクト型のメソッドに引数を渡すときに自動的に適用されます。 ただし、モジュール内の let-bound 関数の場合、パラメーターの型が "フレキシブル型" として宣言されていない限り、アップキャストは自動ではありません。 詳細については、「フレキシブル型」を参照してください。

:> 演算子は静的なキャストを実行します。これは、キャストの成功がコンパイル時に決定されることを意味します。 :> を使用するキャストが正常にコンパイルされた場合、それは有効なキャストであり、実行時にエラーが発生する可能性はありません。

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")

ここでは、条件付きの分岐がそれぞれ TextReaderStreamReader を計算しています。 2 番目の分岐では、既知のターゲット型はメソッドの型の注釈、および最初の分岐からの TextReader です。 つまり、2 番目の分岐にはアップキャストが必要ないことを意味します。

追加の暗黙のアップキャストが使用されるすべての時点で警告を表示するには、警告 3388 (/warnon:3388 またはプロパティ <WarnOn>3388</WarnOn>) を有効にすることができます。

暗黙の数値変換

F# では、多くの場合、変換演算子によって数値型の明示的な拡大が使用されます。 たとえば、暗黙的な拡大は、int8int16 などの他の数値型、または float32 から float64 の場合、あるいはソースとターゲットのいずれかの型が不明である場合に必要です。

ただし、暗黙の拡大は、暗黙的なアップキャストと同じ状況で、64 ビット整数に拡張された 32 ビット整数で許可されます。 たとえば、次のような一般的な 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])

型の推定時にソースとターゲットの両方の型がわかっている場合、int32int64 に、int32nativeint に、int32double に自動的に拡大されます。 したがって、前の例のような場合は、int32 リテラルを使用できます。

Tensor.Create([100; 10; 10])

また、必要に応じて、警告 3389 (/warnon:3389 またはプロパティ <WarnOn>3389</WarnOn>) を有効にして、暗黙的な数値拡大が使用されるすべての時点で警告を表示することもできます。

.NET スタイルの暗黙的な変換

.NET API を使用すると、静的メソッド op_Implicit を定義して、型間の暗黙的な変換を提供できます。 これらは、メソッドに引数を渡す際に F# コードで自動的に適用されます。 たとえば、op_Implicit メソッドを明示的に呼び出す次のコードを考えます。

open System.Xml.Linq

let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants(XName.op_Implicit "Item")

ソース式とターゲット型に対して型を使用できる場合、引数式に対して .NET スタイルの op_Implicit 変換が自動的に適用されます。

open System.Xml.Linq

let purchaseOrder = XElement.Load("PurchaseOrder.xml")
let partNos = purchaseOrder.Descendants("Item")

また、必要に応じて、警告 3395 (/warnon:3395 またはプロパティ <WarnOn>3395</WarnOn>) を有効にして、.NET スタイルの暗黙的な変換が使用されるすべての時点で警告を表示することもできます。

.NET スタイルの op_Implicit の変換は、暗黙的なアップキャストと同じ状況で、メソッド引数以外の式にも自動的に適用されます。 ただし、暗黙的な変換を幅広くまたは不適切に使用すると、型の推定が正しく操作されず、理解しにくいコードになる可能性があります。 このため、引数以外の位置で使用すると、常に警告が生成されます。

メソッド引数以外で .NET スタイルの暗黙の変換が使用されるすべての時点で警告を表示するには、警告 3391 (/warnon:3391 またはプロパティ <WarnOn>3391</WarnOn>) を有効にできます。

暗黙的な変換を使用すると、次の省略可能な警告が表示されます。

  • /warnon:3388 (追加の暗黙的なアップキャスト)
  • /warnon:3389 (暗黙的な数値の拡大)
  • /warnon:3391 (メソッド以外の引数では op_Implicit、既定ではオン)
  • /warnon:3395 (メソッド引数での op_Implicit)

関連項目