インターフェイス (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
によって示されているように、すべてのメンバーが抽象型になります。 抽象メソッドにメソッド本体を指定することはできません。 F# では、インターフェイスにおける既定のメソッドの実装を定義できませんが、C# で定義される既定の実装との互換性があります。 default
キーワードを使用する既定の実装は、インターフェイス以外の基底クラスから継承する場合にのみサポートされます。
インターフェイスの既定のアクセシビリティは、public
になります。
必要に応じて、通常の F# 構文を使用して、各メソッドのパラメーターに名前を付けることができます。
type ISprintable =
abstract member Print: format: string -> unit
上の ISprintable
の例では、Print
メソッドに型が string
の format
という名前の 1 つのパラメーターがあります。
インターフェイスを実装するには、オブジェクト式を使用する方法と型を使用する方法の 2 つがあります。 どちらの場合も、型またはオブジェクト式によって、そのインターフェイスの抽象メソッドにメソッド本体が指定されます。 実装は、インターフェイスが実装される各型に固有です。 したがって、型が異なるインターフェイスのメソッドは互いに異なる場合があります。
軽量構文を使用する場合、定義の開始と終了をマークするキーワード interface
と end
は省略可能です。 これらのキーワードを使用しない場合、コンパイラでは、使用するコンストラクトを分析することで、型がクラスであるかインターフェイスであるかを推測しようとします。 メンバーを定義する、または他のクラス構文を使用する場合、型はクラスとして解釈されます。
.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"
関連項目
.NET