Teilen über


Erbauer

In diesem Artikel wird beschrieben, wie Konstruktoren zum Erstellen und Initialisieren von Klassen- und Strukturobjekten definiert und verwendet werden.

Erstellen von Klassenobjekten

Objekte von Klassentypen weisen Konstruktoren auf. Es gibt zwei Arten von Konstruktoren. Einer ist der primäre Konstruktor, dessen Parameter direkt hinter dem Typnamen in Klammern angezeigt werden. Optional können Sie zusätzliche Konstruktoren mithilfe des new Schlüsselworts angeben. Alle solchen zusätzlichen Konstruktoren müssen den primären Konstruktor aufrufen.

Der primäre Konstruktor enthält let und do Bindungen, die am Anfang der Klassendefinition angezeigt werden. Eine let Bindung deklariert private Felder und Methoden der Klasse; eine do Bindung führt Code aus. Weitere Informationen zu let Bindungen in Klassenkonstruktoren finden Sie unter let "Bindings in Classes". Weitere Informationen zu do Bindungen in Konstruktoren finden Sie unter do "Bindings in Classes".

Unabhängig davon, ob es sich bei dem Konstruktor, den Sie aufrufen möchten, um einen primären Konstruktor oder einen zusätzlichen Konstruktor handelt, können Sie Objekte mithilfe eines new Ausdrucks, mit oder ohne das optionale new Schlüsselwort erstellen. Sie initialisieren Ihre Objekte zusammen mit Konstruktorargumenten, indem Sie entweder die Argumente in der Reihenfolge auflisten und durch Kommas getrennt und in Klammern eingeschlossen werden, oder indem Sie benannte Argumente und Werte in Klammern verwenden. Sie können eigenschaften für ein Objekt auch während der Erstellung des Objekts festlegen, indem Sie die Eigenschaftsnamen verwenden und Werte genauso wie benannte Konstruktorargumente zuweisen.

Der folgende Code veranschaulicht eine Klasse mit einem Konstruktor und verschiedenen Möglichkeiten zum Erstellen von Objekten:

// 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()

Die Ausgabe lautet wie folgt:

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)

Bau von Strukturen

Strukturen folgen allen Regeln von Klassen. Daher können Sie über einen primären Konstruktor verfügen, und Sie können zusätzliche Konstruktoren mithilfe von new. Es gibt jedoch einen wichtigen Unterschied zwischen Strukturen und Klassen: Strukturen können einen parameterlosen Konstruktor aufweisen (d. h. einen ohne Argumente), auch wenn kein primärer Konstruktor definiert ist. Der parameterlose Konstruktor initialisiert alle Felder mit dem Standardwert für diesen Typ, in der Regel 0 oder dessen Entsprechung. Alle Konstruktoren, die Sie für Strukturen definieren, müssen mindestens ein Argument aufweisen, damit sie nicht mit dem parameterlosen Konstruktor in Konflikt stehen.

Außerdem verfügen Strukturen häufig über Felder, die mithilfe des val Schlüsselworts erstellt werden. Klassen können auch diese Felder aufweisen. Strukturen und Klassen, die mithilfe des val Schlüsselworts definierte Felder haben, können auch in zusätzlichen Konstruktoren mithilfe von Datensatzausdrücken initialisiert werden, wie im folgenden Code gezeigt.

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)

Weitere Informationen finden Sie unter Explicit Fields: The val Keyword.

Ausführen von Nebenwirkungen in Konstruktoren

Ein primärer Konstruktor in einer Klasse kann Code in einer do Bindung ausführen. Was ist jedoch, wenn Sie Code in einem zusätzlichen Konstruktor ohne do Bindung ausführen müssen? Dazu verwenden Sie das then Schlüsselwort.

 // 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()

Die Nebenwirkungen des primären Konstruktors werden weiterhin ausgeführt. Daher lautet die Ausgabe wie folgt:

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

Der Grund, warum then anstelle eines anderen do erforderlich ist, besteht darin, dass das do Schlüsselwort seine Standardbedeutung hat, einen unit-rückgabenden Ausdruck zu trennen, wenn er im Textkörper eines zusätzlichen Konstruktors vorhanden ist. Sie hat nur eine besondere Bedeutung im Kontext primärer Konstruktoren.

Selbstbezeichner in Konstruktoren

In anderen Membern geben Sie einen Namen für das aktuelle Objekt in der Definition der einzelnen Member an. Sie können den Selbstbezeichner auch in die erste Zeile der Klassendefinition einfügen, indem Sie das as Schlüsselwort verwenden, das unmittelbar auf die Konstruktorparameter folgt. Das folgende Beispiel veranschaulicht diese Syntax.

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

In zusätzlichen Konstruktoren können Sie auch einen Selbstbezeichner definieren, indem Sie die as Klausel direkt hinter den Konstruktorparametern einfügen. Das folgende Beispiel veranschaulicht diese Syntax:

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

Probleme können auftreten, wenn Sie versuchen, ein Objekt zu verwenden, bevor es vollständig definiert ist. Daher kann die Verwendung des Selbstbezeichners dazu führen, dass der Compiler eine Warnung ausgibt und zusätzliche Prüfungen einfügt, um sicherzustellen, dass die Member eines Objekts nicht aufgerufen werden, bevor das Objekt initialisiert wird. Sie sollten den Selbstbezeichner nur in den do Bindungen des primären Konstruktors oder nach dem then Schlüsselwort in zusätzlichen Konstruktoren verwenden.

Der Name des Selbstbezeichners muss nicht sein this. Dabei kann es sich um einen beliebigen gültigen Bezeichner handeln.

Zuweisen von Werten zu Eigenschaften bei der Initialisierung

Sie können den Eigenschaften eines Klassenobjekts im Initialisierungscode Werte zuweisen, indem Sie eine Liste der Zuordnungen des Formulars property = value an die Argumentliste für einen Konstruktor anfügen. Dies wird im folgenden Codebeispiel gezeigt:

 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)

Die folgende Version des vorherigen Codes veranschaulicht die Kombination von gewöhnlichen Argumenten, optionalen Argumenten und Eigenschafteneinstellungen in einem Konstruktoraufruf:

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")

Konstruktoren in geerbter Klasse

Wenn Sie von einer Basisklasse erben, die über einen Konstruktor verfügt, müssen Sie die zugehörigen Argumente in der Erbklausel angeben. Weitere Informationen finden Sie unter Konstruktoren und Vererbung.

Statische Konstruktoren oder Typkonstruktoren

Zusätzlich zum Angeben von Code zum Erstellen von Objekten können statische let und do Bindungen in Klassentypen erstellt werden, die ausgeführt werden, bevor der Typ zum ersten Mal zur Initialisierung auf Typebene verwendet wird. Weitere Informationen finden Sie unter let Bindungen in Klassen und do Bindungen in Klassen.

Siehe auch