インターフェイス (F#)

他のクラスで実装される関連メンバーのセットは、"インターフェイス" によって指定されます。

構文

// Interface declaration:
[ attributes ]
type [accessibility-modifier] interface-name =
    [ interface ]     [ inherit base-interface-name ...]
    abstract member1 : [ argument-types1 -> ] return-type1
    abstract member2 : [ argument-types2 -> ] return-type2
    ...
[ end ]

// Implementing, inside a class type definition:
interface interface-name with
    member self-identifier.member1argument-list = method-body1
    member self-identifier.member2argument-list = method-body2

// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
    { new interface-name with
        member self-identifier.member1argument-list = method-body1
        member self-identifier.member2argument-list = method-body2
        [ base-interface-definitions ]
    }
    member-list

解説

インターフェイス宣言は、メンバーが実装されていないことを除いて、クラス宣言に似ています。 代わりに、キーワード abstract によって示されているように、すべてのメンバーが抽象型になります。 抽象メソッドにメソッド本体を指定することはできません。 ただし、default キーワードと共にメンバーの別の定義もメソッドとして含めることで、既定の実装を指定できます。 これは、他の .NET 言語で基底クラスの仮想メソッドを作成することと同じです。 このような仮想メソッドは、インターフェイスが実装されるクラスでオーバーライドできます。

インターフェイスの既定のアクセシビリティは、public になります。

必要に応じて、通常の F# 構文を使用して、各メソッドのパラメーターに名前を付けることができます。

type ISprintable =
    abstract member Print : format:string -> unit

上の ISprintable の例では、Print メソッドに型が stringformat という名前の 1 つのパラメーターがあります。

インターフェイスを実装するには、オブジェクト式を使用する方法とクラス型を使用する方法の 2 つがあります。 どちらの場合も、クラス型またはオブジェクト式によって、そのインターフェイスの抽象メソッドにメソッド本体が指定されます。 実装は、インターフェイスが実装される各型に固有です。 したがって、型が異なるインターフェイスのメソッドは互いに異なる場合があります。

軽量構文を使用する場合、定義の開始と終了をマークするキーワード interfaceend は省略可能です。 これらのキーワードを使用しない場合、コンパイラでは、使用するコンストラクトを分析することで、型がクラスであるかインターフェイスであるかを推測しようとします。 メンバーを定義する、または他のクラス構文を使用する場合、型はクラスとして解釈されます。

.NET のコーディング スタイルでは、すべてのインターフェイスを大文字の I で開始します。

F# スタイルと .NET スタイルの 2 つの方法で、複数のパラメーターを指定できます。 どちらも .NET コンシューマーに関しては同じようにコンパイルされますが、F# スタイルでは F# スタイルのパラメーターのアプリケーションを使用するよう F# の呼び出し元に強制し、.NET スタイルではタプル化された引数のアプリケーションを使用するよう F# の呼び出し元に強制します。

type INumericFSharp =
    abstract Add: x: int -> y: int -> int

type INumericDotNet =
    abstract Add: x: int * y: int -> int

クラス型を使用したインターフェイスの実装

次のコードに示すように、interface キーワード、インターフェイスの名前、with キーワードを使用し、その後にインターフェイス メンバーの定義を続けることで、1 つ以上のインターフェイスをクラス型に実装できます。

type IPrintable =
   abstract member Print : unit -> unit

type SomeClass1(x: int, y: float) =
   interface IPrintable with
      member this.Print() = printfn "%d %f" x y

インターフェイスの実装は継承されるため、派生クラスではそれらを再実装する必要はありません。

インターフェイス メソッドの呼び出し

インターフェイス メソッドは、インターフェイスが実装される型のオブジェクトではなく、インターフェイスを通じてのみ呼び出されます。 そのため、これらのメソッドを呼び出すには、:> 演算子または upcast 演算子を使用して、インターフェイス型にアップキャストすることが必要になる場合があります。

SomeClass 型のオブジェクトがあるときにインターフェイス メソッドを呼び出すには、次のコードに示すように、そのオブジェクトをインターフェイス型にアップキャストする必要があります。

let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()

別の方法として、次の例のように、アップキャストしてインターフェイス メソッドが呼び出されるオブジェクトに対して、メソッドを宣言する方法があります。

type SomeClass2(x: int, y: float) =
   member this.Print() = (this :> IPrintable).Print()
   interface IPrintable with
      member this.Print() = printfn "%d %f" x y

let x2 = new SomeClass2(1, 2.0)
x2.Print()

オブジェクト式を使用したインターフェイスの実装

オブジェクト式には、インターフェイスを実装するための簡単な方法があります。 これらは、名前付きの型を作成する必要がなく、追加のメソッドなしにインターフェイス メソッドをサポートするオブジェクトを必要とするのみの場合に便利です。 オブジェクト式を次のコードに示します。

let makePrintable(x: int, y: float) =
    { new IPrintable with
              member this.Print() = printfn "%d %f" x y }
let x3 = makePrintable(1, 2.0)
x3.Print()

インターフェイスの継承

インターフェイスは、1 つ以上の基底インターフェイスから継承できます。

type Interface1 =
    abstract member Method1 : int -> int

type Interface2 =
    abstract member Method2 : int -> int

type Interface3 =
    inherit Interface1
    inherit Interface2
    abstract member Method3 : int -> int

type MyClass() =
    interface Interface3 with
        member this.Method1(n) = 2 * n
        member this.Method2(n) = n + 100
        member this.Method3(n) = n / 10

既定の実装でのインターフェイスの実装

C# では、次のように、既定の実装でインターフェイスを定義できます。

using System;

namespace CSharp
{
    public interface MyDim
    {
        public int Z => 0;
    }
}

これらは F# から直接利用できます。

open CSharp

// You can implement the interface via a class
type MyType() =
    member _.M() = ()

    interface MyDim

let md = MyType() :> MyDim
printfn $"DIM from C#: %d{md.Z}"

// You can also implement it via an object expression
let md' = { new MyDim }
printfn $"DIM from C# but via Object Expression: %d{md'.Z}"

仮想メンバーをオーバーライドするのと同様に、override を使用して既定の実装をオーバーライドできます。

既定の実装がないインターフェイスのメンバーは、引き続き明示的に実装する必要があります。

同じインターフェイスのさまざまなジェネリック インスタンス化での実装

F# では、次のように、同じインターフェイスのさまざまなジェネリック インスタンス化での実装をサポートしています。

type IA<'T> =
    abstract member Get : unit -> 'T

type MyClass() =
    interface IA<int> with
        member x.Get() = 1
    interface IA<string> with
        member x.Get() = "hello"

let mc = MyClass()
let iaInt = mc :> IA<int>
let iaString = mc :> IA<string>

iaInt.Get() // 1
iaString.Get() // "hello"

関連項目