メソッド

"メソッド" は、型に関連付けられている関数です。 オブジェクト指向プログラミングでは、メソッドを使用して、オブジェクトと型の機能と動作を公開および実装します。

構文

// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
    method-body

// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature

// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ([<Optional; DefaultParameterValue( default-value )>] input) [ : return-type ]

解説

前の構文では、さまざまな形式のメソッドの宣言と定義があることを確認できます。 本体が長いメソッドでは、等号 (=) の後に改行を付けることで、メソッド本体全体がインデントされます。

属性は、任意のメソッド宣言に適用できます。 これらはメソッド定義の構文の前にあり、通常は別の行に表示されます。 詳細については、「属性」を参照してください。

メソッドは inline としてマークできます。 inline の詳細については、「Inline Functions」(インライン関数) を参照してください。

インラインでないメソッドは、型の中で再帰的に使用できます。rec キーワードを明示的に使用する必要はありません。

インスタンス メソッド

インスタンス メソッドは、member キーワードと self-identifier、その後にピリオド (.)、メソッド名、パラメーターを指定して宣言します。 let バインディングの場合と同様に、parameter-list はパターンにすることができます。 通常、メソッド パラメーターはタプル形式でかっこで囲みます。他の .NET Framework 言語で作成されたメソッドは、F# にこのように表示されます。 ただし、カリー化形式 (スペースで区切られたパラメーター) も一般的であり、他のパターンもサポートされています。

次の例は、非抽象インスタンス メソッドの定義と使用方法を示しています。

type SomeType(factor0: int) =
    let factor = factor0
    member this.SomeMethod(a, b, c) = (a + b + c) * factor

    member this.SomeOtherMethod(a, b, c) = this.SomeMethod(a, b, c) * factor

インスタンス メソッド内では、let バインディングを使用して定義されたフィールドにアクセスするために、自己識別子を使用しないでください。 自己識別子は、他のメンバーやプロパティにアクセスするときに使用します。

静的メソッド

キーワード static は、メソッドをインスタンスなしで呼び出すことができることと、オブジェクト インスタンスと関連付けられていないことを指定するために使用します。 それ以外の場合、メソッドはインスタンス メソッドです。

次のセクションの例は、let キーワードを使用して宣言されたフィールド、member キーワードを使用して宣言されたプロパティ メンバー、および static キーワードを使用して宣言された静的メソッドを示しています。

次の例は、静的メソッドの定義と使用方法を示しています。 これらのメソッド定義は、前のセクションの SomeType クラスに含まれているものとします。

static member SomeStaticMethod(a, b, c) =
   (a + b + c)

static member SomeOtherStaticMethod(a, b, c) =
   SomeType.SomeStaticMethod(a, b, c) * 100

抽象メソッドと仮想メソッド

キーワード abstract は、メソッドに仮想ディスパッチ スロットがあり、クラスに定義がない可能性があることを示します。 "仮想ディスパッチ スロット" は、オブジェクト指向の型の仮想関数呼び出しを検索するために実行時に使用される、内部的に管理された関数のテーブル内のエントリです。 仮想ディスパッチ メカニズムは、オブジェクト指向プログラミングの重要な機能である "ポリモーフィズム" が実装されるメカニズムです。 定義のない抽象メソッドを 1 つ以上持つクラスは "抽象クラス" であるため、そのクラスのインスタンスを作成することはできません。 抽象クラスの詳細については、「抽象クラス」を参照してください。

抽象メソッドの宣言には、メソッド本体は含まれません。 代わりに、メソッドの名前の後にコロン (:) とそのメソッドの型シグネチャが続きます。 メソッドの型シグネチャは、パラメーター名がないのを除き、Visual Studio Code エディターでメソッド名の上にマウス ポインターを置くと IntelliSense によって表示されるものと同じです。 また、型のシグネチャは、対話形式で作業しているときにインタープリター (fsi.exe) によっても表示されます。 メソッドの型シグネチャは、パラメーターの型を一覧表示し、その後に戻り値の型を適切な区切り記号で指定することで形成されます。 カリー化されたパラメーターは -> で区切られ、タプルのパラメーターは * で区切られます。 戻り値は、常に -> 記号によって引数から区切られます。 かっこは、関数の型がパラメーターであるときや、1 つのタプルが 2 つのパラメーターとしてではなく 1 つのパラメーターとして扱われるときを示すなど、複雑なパラメーターをグループ化するために使用できます。

このトピックの構文ブロックに示すように、クラスに定義を追加し、default キーワードを使用することで、抽象メソッドの既定の定義を指定することもできます。 同じクラス内に定義を持つ抽象メソッドは、.NET Framework の他の言語の仮想メソッドに相当します。 定義が存在するかどうかにかかわらず、abstract キーワードによって、クラスの仮想関数テーブルに新しいディスパッチ スロットが作成されます。

基底クラスで抽象メソッドが実装されるかどうかに関係なく、派生クラスでは抽象メソッドの実装を指定できます。 派生クラスで抽象メソッドを実装するには、その派生クラスに同じ名前とシグネチャを持つメソッドを定義します。ただし、override または default キーワードを使用し、メソッド本体を指定します。 キーワード overridedefault の意味はまったく同じです。 新しいメソッドによって基底クラスの実装がオーバーライドされる場合は、override を使用します。元の抽象宣言と同じクラスに実装を作成するときは、default を使用します。 基底クラスで抽象として宣言されたメソッドが実装されるメソッドでは、abstract キーワードを使用しないでください。

次の例は、既定の実装を持つ抽象メソッド Rotate を示しています。これは .NET Framework の仮想メソッドに相当します。

type Ellipse(a0: float, b0: float, theta0: float) =
    let mutable axis1 = a0
    let mutable axis2 = b0
    let mutable rotAngle = theta0
    abstract member Rotate: float -> unit
    default this.Rotate(delta: float) = rotAngle <- rotAngle + delta

次の例は、基底クラスのメソッドをオーバーライドする派生クラスを示します。 この場合、オーバーライドによって動作が変更され、メソッドが何も実行しないようになります。

type Circle(radius: float) =
    inherit Ellipse(radius, radius, 0.0)
    // Circles are invariant to rotation, so do nothing.
    override this.Rotate(_) = ()

オーバーロードされたメソッド

オーバーロードされたメソッドは、指定された型の名前が同じで、引数が異なるメソッドです。 F# では、通常はオーバーロードされたメソッドの代わりに、省略可能な引数が使用されます。 ただし、オーバーロードされたメソッドは、引数がカリー化形式ではなくタプル形式である場合に、その言語で許可されます。

省略可能な引数

F# 4.1 以降では、メソッドに既定のパラメーター値を持つ省略可能な引数を指定することもできます。 これは、C# コードとの相互運用を容易にするためのものです。 次の例にその関数を示します。

open System.Runtime.InteropServices
// A class with a method M, which takes in an optional integer argument.
type C() =
    member _.M([<Optional; DefaultParameterValue(12)>] i) = i + 1

DefaultParameterValue に渡される値は、入力の型と一致する必要があることに注意してください。 上記のサンプルでは、これは int です。 DefaultParameterValue に整数以外の値を渡そうとすると、コンパイル エラーが発生します。

例: プロパティとメソッド

次の例に示す型には、フィールド、プライベート関数、プロパティ、および静的メソッドの例が含まれています。

type RectangleXY(x1: float, y1: float, x2: float, y2: float) =
    // Field definitions.
    let height = y2 - y1
    let width = x2 - x1
    let area = height * width
    // Private functions.
    static let maxFloat (x: float) (y: float) = if x >= y then x else y
    static let minFloat (x: float) (y: float) = if x <= y then x else y
    // Properties.
    // Here, "this" is used as the self identifier,
    // but it can be any identifier.
    member this.X1 = x1
    member this.Y1 = y1
    member this.X2 = x2
    member this.Y2 = y2
    // A static method.
    static member intersection(rect1: RectangleXY, rect2: RectangleXY) =
        let x1 = maxFloat rect1.X1 rect2.X1
        let y1 = maxFloat rect1.Y1 rect2.Y1
        let x2 = minFloat rect1.X2 rect2.X2
        let y2 = minFloat rect1.Y2 rect2.Y2

        let result: RectangleXY option =
            if (x2 > x1 && y2 > y1) then
                Some(RectangleXY(x1, y1, x2, y2))
            else
                None

        result

// Test code.
let testIntersection =
    let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
    let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
    let r3: RectangleXY option = RectangleXY.intersection (r1, r2)

    match r3 with
    | Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
    | None -> printfn "No intersection found."

testIntersection

関連項目