インターフェイスは 、他のクラスが実装する関連メンバーのセットを指定します。
構文
// 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 スタイル。 両方とも .NET コンシューマーに対して同じ方法でコンパイルされますが、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
インターフェイスの実装は継承されるため、派生クラスを再実装する必要はありません。
空のインターフェイスまたはマーカー インターフェイスを定義する
マーカー インターフェイスとも呼ばれる空のインターフェイスを使用すると、特定の動作を必要とせずに一連の型を識別できます。 これらのインターフェイスにはメンバーがなく、分類の目的で型をマークまたはタグ付けする方法として機能します。
空のインターフェイスは、 interface end 構文を使用して定義します。
// Define an empty interface (also known as a marker interface)
type IMarker =
interface end
// Implement the empty interface in a record type
type MyRecord =
{ Name: string }
interface IMarker
// Implement the empty interface in a class type
type MyClass(value: int) =
member _.Value = value
interface IMarker
上記の例では、 IMarker は空のインターフェイスとして定義されています。 インターフェイスにはメンバーがないため、 MyRecord と MyClass の両方で、メソッドの実装を提供する必要なく、このインターフェイスを実装します。 これにより、インターフェイス型を使用して、これらの型を一般的な方法で識別して操作できます。
インターフェイス メソッドの呼び出し
インターフェイス メソッドは、インターフェイスを実装する型のオブジェクトを介してではなく、インターフェイス経由でのみ呼び出すことができます。 したがって、これらのメソッドを呼び出すには、 :> 演算子または 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