F#では、関数型コードをオブジェクト指向コードよりも一般的に優先すべきなのは明白ですが、状況によっては、クラス、継承、仮想メソッドなどの完全なオブジェクト指向言語の機能がすべて必要になる場合もあります。

そこで、このセクションの締めくくりとして、これらの機能のF#版を駆け足で見ていきましょう。

これらの一部については、後の.NET統合に関するシリーズでもっと詳しく扱います。ただし、あまり使われない機能については触れません。必要になった場合はMicrosoft Learnのドキュメントを参照してください。

クラスとインターフェース

まず、インターフェース、抽象クラス、そして抽象クラスを継承した具象クラスの例を見てみましょう。

// インターフェース
type IEnumerator<'a> = 
    abstract member Current : 'a
    abstract MoveNext : unit -> bool 

// 仮想メソッドを持つ抽象基底クラス
[<AbstractClass>]
type Shape() = 
    // 読み取り専用プロパティ
    abstract member Width : int with get
    abstract member Height : int with get
    // 非仮想メソッド
    member this.BoundingArea = this.Height * this.Width
    // 基本実装を持つ仮想メソッド
    abstract member Print : unit -> unit 
    default this.Print () = printfn "私は図形です"

// 基底クラスを継承してオーバーライドする具象クラス
type Rectangle(x:int, y:int) = 
    inherit Shape()
    override this.Width = x
    override this.Height = y
    override this.Print ()  = printfn "私は長方形です"

// テスト
let r = Rectangle(2,3)
printfn "幅は %i です" r.Width
printfn "面積は %i です" r.BoundingArea
r.Print()

クラスは複数のコンストラクタ、可変プロパティなどを持つことができます。

type Circle(rad:int) = 
    inherit Shape()

    // 可変フィールド
    let mutable radius = rad

    // プロパティのオーバーライド
    override this.Width = radius * 2
    override this.Height = radius * 2

    // デフォルトの半径を持つ別のコンストラクタ
    new() = Circle(10)      

    // getとsetを持つプロパティ
    member this.Radius
         with get() = radius
         and set(value) = radius <- value

// コンストラクタのテスト
let c1 = Circle()   // パラメータなしのコンストラクタ
printfn "幅は %i です" c1.Width
let c2 = Circle(2)  // メインのコンストラクタ
printfn "幅は %i です" c2.Width

// 可変プロパティのテスト
c2.Radius <- 3
printfn "幅は %i です" c2.Width

ジェネリクス

F#はジェネリクスと関連するすべての制約をサポートしています。

// 標準的なジェネリクス
type KeyValuePair<'a,'b>(key:'a, value: 'b) = 
    member this.Key = key
    member this.Value = value

// 制約付きジェネリクス
type Container<'a,'b 
    when 'a : equality 
    and 'b :> System.Collections.ICollection>
    (name:'a, values:'b) = 
    member this.Name = name
    member this.Values = values

構造体

F#はクラスだけでなく、.NETの構造体型もサポートしており、特定のケースでパフォーマンスを向上させるのに役立ちます。


type Point2D =
   struct
      val X: float
      val Y: float
      new(x: float, y: float) = { X = x; Y = y }
   end

// テスト
let p = Point2D()  // ゼロで初期化
let p2 = Point2D(2.0,3.0)  // 明示的に初期化

例外

F#では例外クラスを作成し、それらを投げたり捕捉したりできます。

// 新しい例外クラスを作成
exception MyError of string

try
    let e = MyError("おっと!")
    raise e
with 
    | MyError msg -> 
        printfn "例外のエラーは %s でした" msg
    | _ -> 
        printfn "その他の例外です"

拡張メソッド

C#と同様に、F#でも既存のクラスを拡張メソッドで拡張できます。

type System.String with
    member this.StartsWithA = this.StartsWith "A"

// テスト
let s = "Alice"
printfn "'%s' はAで始まる = %A" s s.StartsWithA

type System.Int32 with
    member this.IsEven = this % 2 = 0

// テスト
let i = 20
if i.IsEven then printfn "'%i' は偶数です" i

パラメータ配列

C#の可変長引数キーワード params と同様に、この機能は可変長の引数リストを単一の配列パラメーターに変換できます。

open System
type MyConsole() =
    member this.WriteLine([<ParamArray>] args: Object[]) =
        for arg in args do
            printfn "%A" arg

let cons = new MyConsole()
cons.WriteLine("abc", 42, 3.14, true)

イベント

F#のクラスはイベントを持つことができ、イベントをトリガーしたり応答したりできます。

type MyButton() =
    let clickEvent = new Event<_>()

    [<CLIEvent>]
    member this.OnClick = clickEvent.Publish

    member this.TestEvent(arg) =
        clickEvent.Trigger(this, arg)

// テスト
let myButton = new MyButton()
myButton.OnClick.Add(fun (sender, arg) -> 
        printfn "引数 %O でクリックイベントが発生" arg)

myButton.TestEvent("こんにちは、世界!")

デリゲート

F#はデリゲートを扱えます。

// デリゲート
type MyDelegate = delegate of int -> int
let f = MyDelegate (fun x -> x * x)
let result = f.Invoke(5)

列挙型

F#はCLIの列挙型をサポートしています。これは「判別共用体」型に似ていますが、内部的には異なります。

// 列挙型
type Color = | Red=1 | Green=2 | Blue=3

let color1  = Color.Red    // 単純な代入
let color2:Color = enum 2  // intからのキャスト
// 文字列の解析から作成
let color3 = System.Enum.Parse(typeof<Color>,"Green") :?> Color // :?> はダウンキャスト

[<System.FlagsAttribute>]
type FileAccess = | Read=1 | Write=2 | Execute=4 
let fileaccess = FileAccess.Read ||| FileAccess.Write

標準ユーザーインターフェースの操作

最後に、F#はC#と同様に、WinFormsやWPFのユーザーインターフェースライブラリを操作できます。

以下は、フォームを開いてクリックイベントを処理する簡単な例です。

open System.Windows.Forms 

let form = new Form(Width= 400, Height = 300, Visible = true, Text = "こんにちは、世界") 
form.TopMost <- true
form.Click.Add (fun args-> printfn "フォームがクリックされました")
form.Show()

results matching ""

    No results matching ""