クラス は、プロパティ、メソッド、およびイベントを持つオブジェクトを表す型です。
構文
// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
注釈
クラスは、.NET オブジェクト型の基本的な説明を表します。このクラスは、F# でのオブジェクト指向プログラミングをサポートする主な型の概念です。
上記の構文では、 type-name
は任意の有効な識別子です。
type-params
では、省略可能なジェネリック型パラメーターについて説明します。 これは、型パラメーター名と、山かっこで囲まれた制約 (<
と >
) で構成されます。 詳細については、「ジェネリックと制約」を参照してください。
parameter-list
では、コンストラクターパラメーターについて説明します。 最初のアクセス修飾子は型に関連します。2 つ目は、プライマリ コンストラクターに関連します。 どちらの場合も、既定値は public
です。
inherit
キーワードを使用して、クラスの基底クラスを指定します。 基底クラスコンストラクターの引数をかっこで囲んで指定する必要があります。
let
バインドを使用して、クラスに対してローカルなフィールドまたは関数の値を宣言し、let
バインドの一般的な規則に従う必要があります。
do-bindings
セクションには、オブジェクトの構築時に実行されるコードが含まれています。
member-list
は、追加のコンストラクター、インスタンスと静的メソッドの宣言、インターフェイス宣言、抽象バインディング、およびプロパティとイベントの宣言で構成されます。 これらは 「メンバー」で説明されています。
省略可能な as
キーワードと共に使用されるidentifier
は、インスタンス変数に名前を付けます。自己識別子は、型定義で型のインスタンスを参照するために使用できます。 詳細については、このトピックの「自己識別子」セクションを参照してください。
定義の開始と終了を示すキーワード class
と end
は省略可能です。
相互再帰型 (相互に参照する型) は、相互再帰関数と同様に、 and
キーワードと共に結合されます。 例については、「相互再帰型」セクションを参照してください。
コンストラクター
コンストラクターは、クラス型のインスタンスを作成するコードです。 クラスのコンストラクターは、F# では他の .NET 言語とは動作が多少異なります。 F# クラスには常に、型名に続く引数が parameter-list
で記述され、その本体がクラス宣言の開始時に let
(および let rec
) バインディングと後続の do
バインドで構成されるプライマリ コンストラクターがあります。 プライマリ コンストラクターの引数は、クラス宣言全体のスコープ内にあります。
次のように、 new
キーワードを使用してメンバーを追加することで、コンストラクターを追加できます。
new
(argument-list
) = constructor-body
新しいコンストラクターの本体は、クラス宣言の先頭で指定されたプライマリ コンストラクターを呼び出す必要があります。
次の例は、この概念を示しています。 次のコードでは、 MyClass
には 2 つのコンストラクターがあり、2 つの引数を受け取るプライマリ コンストラクターと、引数を受け取たない別のコンストラクターがあります。
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
let and do Bindings
クラス定義の let
バインドと do
バインドは、プライマリ クラス コンストラクターの本体を形成するため、クラス インスタンスが作成されるたびに実行されます。
let
バインディングが関数の場合は、メンバーにコンパイルされます。
let
バインディングが関数またはメンバーで使用されていない値である場合、コンストラクターに対してローカルな変数にコンパイルされます。 それ以外の場合は、クラスのフィールドにコンパイルされます。 次の do
式は、プライマリ コンストラクターにコンパイルされ、すべてのインスタンスの初期化コードを実行します。 追加のコンストラクターは常にプライマリ コンストラクターを呼び出すので、呼び出されるコンストラクターに関係なく、 let
バインドと do
バインドは常に実行されます。
let
バインドによって作成されたフィールドは、クラスのメソッドとプロパティ全体でアクセスできます。ただし、静的メソッドがインスタンス変数をパラメーターとして受け取る場合でも、静的メソッドからアクセスすることはできません。 存在する場合、自己識別子を使用してアクセスすることはできません。
自己識別子
自己識別子は、現在のインスタンスを表す名前です。 自己識別子は、C# または C++ の this
キーワードや Visual Basic の Me
に似ています。 自己識別子をクラス定義全体のスコープにするか、個々のメソッドのスコープにするかに応じて、2 つの異なる方法で自己識別子を定義できます。
クラス全体の自己識別子を定義するには、コンストラクター パラメーター リストの終わりかっこの後に as
キーワードを使用し、識別子名を指定します。
1 つのメソッドに対して自己識別子を定義するには、メンバー宣言で、メソッド名の直前に自己識別子を指定し、区切り記号としてピリオド (.) を指定します。
次のコード例は、自己識別子を作成する 2 つの方法を示しています。 最初の行では、 as
キーワードを使用して自己識別子を定義します。 5 行目では、 this
識別子を使用して、スコープがメソッド PrintMessage
に制限されている自己識別子を定義します。
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
他の .NET 言語とは異なり、必要に応じて自己識別子に名前を付けることができます。 self
、 Me
、 this
などの名前に制限されません。
as
キーワードで宣言された自己識別子は、基本コンストラクターの後まで初期化されません。 したがって、基本コンストラクターの前または内部で使用すると、実行時に System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.
が発生します。
let
バインディングやdo
バインドなど、基本コンストラクターの後で自己識別子を自由に使用できます。
ジェネリック型の型パラメーター
ジェネリック型パラメーターは、角かっこ (<
と >
) で、単一引用符の後に識別子の形式で指定されます。 複数のジェネリック型パラメーターはコンマで区切られます。 ジェネリック型パラメーターは、宣言全体のスコープ内にあります。 次のコード例は、ジェネリック型パラメーターを指定する方法を示しています。
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
型引数は、型が使用されるときに推論されます。 次のコードでは、推論される型はタプルのシーケンスです。
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
継承の指定
inherit
句は、直接基底クラスがある場合は識別します。 F# では、直接基底クラスは 1 つだけ許可されます。 クラスが実装するインターフェイスは、基底クラスとは見なされません。 インターフェイスについては、「 インターフェイス」 トピックで説明します。
派生クラスから基底クラスのメソッドとプロパティにアクセスするには、識別子として language キーワード base
を使用し、その後にピリオド (.) とメンバーの名前を付けます。
詳細については、「継承」を参照してください。
[メンバー] セクション
このセクションでは、静的メソッドまたはインスタンス メソッド、プロパティ、インターフェイス実装、抽象メンバー、イベント宣言、および追加のコンストラクターを定義できます。 Let バインドと do バインディングは、このセクションでは表示できません。 メンバーはクラスに加えてさまざまな F# 型に追加できるため、別のトピック「 メンバー」で説明します。
相互再帰型
相互に循環して参照する型を定義する場合は、 and
キーワードを使用して型定義を文字列化します。
and
キーワードは、最初の定義を除くすべての type
キーワードを次のように置き換えます。
open System.IO
type Folder(pathIn: string) =
let path = pathIn
let filenameArray: string array = Directory.GetFiles(path)
member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray
and File(filename: string, containingFolder: Folder) =
member this.Name = filename
member this.ContainingFolder = containingFolder
let folder1 = new Folder(".")
for file in folder1.FileArray do
printfn "%s" file.Name
出力は、現在のディレクトリ内のすべてのファイルの一覧です。
クラス、共用体、レコード、および構造体を使用する場合
さまざまな種類から選択する場合は、特定の状況に適した型を選択するために、各型の設計内容を十分に理解する必要があります。 クラスは、オブジェクト指向プログラミング コンテキストで使用するように設計されています。 オブジェクト指向プログラミングは、.NET Framework 用に記述されたアプリケーションで使用される主要なパラダイムです。 F# コードが .NET Framework または他のオブジェクト指向ライブラリと密接に連携する必要がある場合、特に UI ライブラリなどのオブジェクト指向型システムから拡張する必要がある場合は、クラスが適している可能性があります。
オブジェクト指向コードと密接に相互運用していない場合、または自己完結型でオブジェクト指向コードとの頻繁な対話から保護されているコードを記述する場合は、クラス、レコード、判別共用体の組み合わせを使用することを検討する必要があります。 適切なパターン マッチング コードと共に、よく考え抜いた 1 つの判別共用体は、多くの場合、オブジェクト階層のより単純な代替手段として使用できます。 判別共用体の詳細については、「判別 共用体」を参照してください。
レコードはクラスよりも単純であるという利点がありますが、型の要求が単純さを使用して達成できる量を超える場合、レコードは適切ではありません。 レコードは基本的に値の単純な集計であり、カスタム アクションを実行できる個別のコンストラクターを使用せず、非表示フィールドを使用せず、継承やインターフェイスの実装も実行できません。 プロパティやメソッドなどのメンバーをレコードに追加して動作を複雑にすることはできますが、レコードに格納されているフィールドは値の単純な集計です。 レコードの詳細については、「 レコード」を参照してください。
構造体はデータの小さな集計にも役立ちますが、.NET 値型であるという点でクラスやレコードとは異なります。 クラスとレコードは .NET 参照型です。 値型と参照型のセマンティクスは、値型が値渡しされる点で異なります。 これは、パラメーターとして渡されるか、関数から返されるときにビット単位でコピーされることを意味します。 また、これらはスタックに格納されるか、フィールドとして使用される場合は、ヒープ上の独自の別の場所に格納されるのではなく、親オブジェクト内に埋め込まれます。 そのため、ヒープへのアクセスのオーバーヘッドが問題である場合は、頻繁にアクセスされるデータに構造体が適しています。 構造体の詳細については、「 構造体」を参照してください。
こちらも参照ください
.NET