コンストラクター

この記事では、コンストラクターを定義して使用して、クラスおよび構造体オブジェクトの作成と初期化を行う方法について説明します。

クラス オブジェクトの構築

クラス型のオブジェクトにはコンストラクターがあります。 2 種類のコンストラクターがあります。 1 つはプライマリ コンストラクターであり、そのパラメーターは型名の直後にかっこで囲んで指定します。 その他のオプションの追加コンストラクターは、new キーワードを使用して指定します。 このような追加のコンストラクターでは、プライマリ コンストラクターを呼び出す必要があります。

プライマリ コンストラクターには letdo のバインドが含まれ、クラス定義の先頭で指定されます。 let バインドによって、クラスのプライベート フィールドとメソッドが宣言されます。do バインドによって、コードが実行されます。 クラス コンストラクターの let バインドの詳細については、「クラス内の let 束縛」を参照してください。 コンストラクターの do バインドの詳細については、「クラス内の do 束縛」を参照してください。

呼び出すコンストラクターがプライマリ コンストラクターか追加コンストラクターかに関係なく、new 式を使用してオブジェクトを作成できます。省略可能な new キーワードは、あってもなくてもかまいません。 コンストラクター引数を使用してオブジェクトを初期化するには、引数をコンマで区切って順番に指定してかっこで囲むか、名前付き引数と値をかっこで囲んで指定します。 また、名前付きコンストラクター引数を使用するのと同じように、オブジェクトを構築するときにプロパティ名と値の割り当てを使用して、オブジェクトのプロパティを設定することもできます。

次のコードは、コンストラクターを持つクラスと、オブジェクトのさまざまな作成方法を示したものです。

// This class has a primary constructor that takes three arguments
// and an additional constructor that calls the primary constructor.
type MyClass(x0, y0, z0) =
    let mutable x = x0
    let mutable y = y0
    let mutable z = z0
    do
        printfn "Initialized object that has coordinates (%d, %d, %d)" x y z
    member this.X with get() = x and set(value) = x <- value
    member this.Y with get() = y and set(value) = y <- value
    member this.Z with get() = z and set(value) = z <- value
    new() = MyClass(0, 0, 0)

// Create by using the new keyword.
let myObject1 = new MyClass(1, 2, 3)
// Create without using the new keyword.
let myObject2 = MyClass(4, 5, 6)
// Create by using named arguments.
let myObject3 = MyClass(x0 = 7, y0 = 8, z0 = 9)
// Create by using the additional constructor.
let myObject4 = MyClass()

出力は次のようになります。

Initialized object that has coordinates (1, 2, 3)
Initialized object that has coordinates (4, 5, 6)
Initialized object that has coordinates (7, 8, 9)
Initialized object that has coordinates (0, 0, 0)

構造体の構築

構造体は、クラスのすべての規則に従います。 したがって、プライマリ コンストラクターを指定することができ、new を使用して追加のコンストラクターを指定することもできます。 ただし、構造体とクラスには重要な違いが 1 つあります。構造体には、プライマリ コンストラクターが定義されていない場合でも、パラメーターなしのコンストラクター (つまり、引数がないもの) を指定することができます。 パラメーターなしのコンストラクターを使用すると、すべてのフィールドがその型の既定値 (通常は 0 またはそれと等価) に初期化されます。 構造体に対して定義するコンストラクターには、パラメーターなしのコンストラクターと競合しないように、少なくとも 1 つの引数が必要です。

また、多くの場合、構造体には val キーワードを使用して作成されるフィールドがあります。クラスでもこれらのフィールドを使用できます。 val キーワードを使用して定義されたフィールドがある構造体とクラスは、次のコードで示すように、レコード式を使用して追加のコンストラクターで初期化することもできます。

type MyStruct =
    struct
       val X : int
       val Y : int
       val Z : int
       new(x, y, z) = { X = x; Y = y; Z = z }
    end

let myStructure1 = new MyStruct(1, 2, 3)

詳細については、「明示的なフィールド: val キーワード」を参照してください。

コンストラクターでの副作用の実行

クラスのプライマリ コンストラクターでは、do バインドでコードを実行できます。 それでは、do バインドのない追加のコンストラクターでコードを実行する必要がある場合はどうすればよいでしょうか。 これを行うには、then キーワードを使用します。

 // Executing side effects in the primary constructor and
// additional constructors.
type Person(nameIn : string, idIn : int) =
    let mutable name = nameIn
    let mutable id = idIn
    do printfn "Created a person object."
    member this.Name with get() = name and set(v) = name <- v
    member this.ID with get() = id and set(v) = id <- v
    new() =
        Person("Invalid Name", -1)
        then
            printfn "Created an invalid person object."

let person1 = new Person("Humberto Acevedo", 123458734)
let person2 = new Person()

プライマリ コンストラクターの副作用は引き続き実行されます。 したがって、出力は次のようになります。

Created a person object.
Created a person object.
Created an invalid person object.

別の do ではなく then が必要な理由は、do キーワードには、追加コンストラクターの本体に存在するときは unit を返す式を区切るという、標準の意味があるためです。 これは、プライマリ コンストラクターのコンテキストでのみ特別な意味を持ちます。

コンストラクター内の自己識別子

他のメンバーでは、各メンバーの定義で現在のオブジェクトの名前を指定します。 クラス定義の最初の行の、コンストラクターのパラメーターの直後に、as キーワードを使用して、自己識別子を配置することもできます。 次に示すのはこの構文の例です。

type MyClass1(x) as this =
    // This use of the self identifier produces a warning - avoid.
    let x1 = this.X
    // This use of the self identifier is acceptable.
    do printfn "Initializing object with X =%d" this.X
    member this.X = x

追加のコンストラクターでも、コンストラクターのパラメーターの直後に as 句を配置することで、自己識別子を定義できます。 次に示すのはこの構文の例です。

type MyClass2(x : int) =
    member this.X = x
    new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X

オブジェクトを完全に定義する前に使用しようとすると、問題が発生する可能性があります。 したがって、自己識別子を使用すると、コンパイラで警告が発生し、オブジェクトが初期化される前にオブジェクトのメンバーがアクセスされないことを確認するための追加のチェックが挿入される場合があります。 自己識別子は、プライマリ コンストラクターの do バインド内、または追加コンストラクターの then キーワードの後でのみ、使用してください。

自己識別子の名前は、this である必要はありません。 任意の有効な識別子を使用できます。

初期化時のプロパティへの値の割り当て

コンストラクターの引数リストに property = value の形式で割り当てのリストを追加することにより、初期化コードでクラス オブジェクトのプロパティに値を割り当てることができます。 次のコード例にこれを示します。

 type Account() =
    let mutable balance = 0.0
    let mutable number = 0
    let mutable firstName = ""
    let mutable lastName = ""
    member this.AccountNumber
       with get() = number
       and set(value) = number <- value
    member this.FirstName
       with get() = firstName
       and set(value) = firstName <- value
    member this.LastName
       with get() = lastName
       and set(value) = lastName <- value
    member this.Balance
       with get() = balance
       and set(value) = balance <- value
    member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
    member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount


let account1 = new Account(AccountNumber=8782108,
                           FirstName="Darren", LastName="Parker",
                           Balance=1543.33)

前のコードの次に示すバージョンは、通常の引数、省略可能な引数、およびプロパティの設定を、1 つのコンストラクター呼び出しで組み合わせる方法を示しています。

type Account(accountNumber : int, ?first: string, ?last: string, ?bal : float) =
   let mutable balance = defaultArg bal 0.0
   let mutable number = accountNumber
   let mutable firstName = defaultArg first ""
   let mutable lastName = defaultArg last ""
   member this.AccountNumber
      with get() = number
      and set(value) = number <- value
   member this.FirstName
      with get() = firstName
      and set(value) = firstName <- value
   member this.LastName
      with get() = lastName
      and set(value) = lastName <- value
   member this.Balance
      with get() = balance
      and set(value) = balance <- value
   member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
   member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount


let account1 = new Account(8782108, bal = 543.33,
                          FirstName="Raman", LastName="Iyer")

継承されたクラスでのコンストラクター

コンストラクターを持つ基底クラスから継承するときは、その引数を inherit 句で指定する必要があります。 詳細については、「コンストラクターと継承」を参照してください。

静的コンストラクターまたは型コンストラクター

オブジェクトを作成するためのコードを指定するだけでなく、型が最初に使用される前に実行する静的な let および do バインドをクラス型内に作成して、型レベルで初期化を実行することもできます。 詳細については、「クラス内の let 束縛」と「クラス内の do 束縛」を参照してください。

関連項目