型拡張の概要
型拡張は、既に定義されているオブジェクト型に継承を使わずに新しいメンバを追加するための機能です。型拡張には3つの手法があり、それぞれ適したシナリオがあります。
- 組み込み型拡張
- オプション型拡張
- 拡張メソッド
組み込み型拡張
組み込み型拡張は、ユーザ定義型を拡張する型拡張機能です。
組み込み型拡張を定義するには、拡張したい型と「同じファイル」 or 「同じ名前空間」 or 「同じモジュール内」で定義する必要があります。それ以外で定義した場合、オプション型拡張となります。
つまり、組み込み型拡張もオプション型拡張は「定義する場所」だけが異なり、定義方法は同じと言えます。以下は、組み込み型拡張とオプション型拡張の定義構文の紹介と、簡単なサンプルとなります。
// 構文
type typename with
member self_identifier.member_name = body
...
namespace InstrinsicExtensionsExample
type Variant =
| Number of int
| String of string
module Variant =
let print = function
| Number n -> printfn "Number: %d" n
| String s -> printfn "String: %s" s
// 組み込み型拡張の定義例
type Variant with
member self.Print () = Variant.print self
// ex: let n = Number 10
// n.Print () // output: Number: 10
オプション型拡張
前述のとおりオプション型拡張は、「元のファイル」 or 「元のモジュール」 or 「元の名前空間」 以外の場所で型を拡張したい場合に利用される手法です。多くの場合、拡張したい型が別のアセンブリで定義されていると思います。つまり、プリミティブ型や他人が作ったライブラリで定義されている型などに対して型拡張したいときに利用します。
定義の方法については、 組み込み型拡張 と同様なため、以下には簡単なサンプルのみを示します。
module Extensions
open System.Collections.Generic
// オプション型拡張の定義例
type IEnumerable<T> with
member self.RepeatElements (n: int) =
seq {
for x in self do
for i in 1..n do
yield x
}
// ex: let ary = [| 0..10 |]
// ary.RepeatElements 3
// |> Seq.toArray
// |> printfn "%A"
// // output: [|0; 0; 0; 1; 1; 1; 2; 2; 2; 3; 3; 3; 4; 4; 4; 5; 5; 5; 6; 6; 6; 7; 7; 7; 8; 8; 8; 9; 9; 9; 10; 10; 10|]
拡張メソッド
C#でお馴染みの拡張メソッドをF#で定義することも可能です。拡張メソッドは、クラスのstaticメンバメソッドとして宣言することで、対象の型に対して機能拡張をすることができます。
以下は、拡張メソッドの定義構文の紹介と、簡単なサンプルとなります。
// 構文
open System.Runtime.CompilerServices
[<Extension>]
type Extensions() =
[<Extension>]
static member inline self_identifier.extension_name (ty: typename, [args]) =
body
...
open System.Runtime.CompilerServices
// 拡張メソッドの定義例
[<Extension>]
type IEnumerableExtensions () =
[<Extension>]
static member inline Sum (xs: IEnumerable<'T>) = Seq.sum xs
[1..10].Sum()
|> printfn "%d" // output: 55