次の方法で共有


コンストラクター

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

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

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

プライマリ コンストラクターには、クラス定義の先頭に表示される let バインドと do バインドが含まれています。 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」を参照してください。

こちらも参照ください