Construtores
Este artigo descreve como definir e usar construtores no F# para criar e inicializar objetos de classe e estrutura.
Construção de objetos de classe
Os objetos de tipos de classe têm construtores. Há dois tipos de construtores. Um deles é o construtor primário, cujos parâmetros são exibidos entre parênteses logo após o nome do tipo. Você pode especificar outros construtores opcionais usando a palavra-chave new
. Qualquer construtor adicional deve chamar o construtor primário.
O construtor primário contém as associações let
e do
, que são exibidas no início da definição de classe. Uma associação let
declara os campos privados e métodos da classe. Uma associação do
executa o código. Para obter mais informações sobre as associações let
nos construtores de classe, confira Associações let
em Classes. Para obter mais informações sobre as associações do
nos construtores, confira Associações do
em Classes.
Independentemente de se o construtor que você deseja chamar é primário ou adicional, você pode criar objetos usando uma expressão new
, com ou sem a palavra-chave opcional new
. Você inicializará os objetos, juntamente com os argumentos de construtor, listando os argumentos em ordem, separados por vírgulas e entre parênteses ou usando argumentos nomeados e valores entre parênteses. Você também pode definir propriedades em um objeto, durante a construção do objeto, usando os nomes de propriedade e atribuindo valores, assim como você usa os argumentos de construtor nomeados.
O código a seguir ilustra uma classe que tem um construtor e várias maneiras de criar objetos:
// 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()
A saída é da seguinte maneira:
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)
Construção de estruturas
As estruturas seguem todas as regras das classes. Portanto, você pode ter um construtor primário e fornecer construtores adicionais usando new
. No entanto, há uma diferença importante entre estruturas e classes: as estruturas podem ter um construtor sem parâmetros (ou seja, sem argumentos), mesmo que nenhum construtor primário seja definido. O construtor sem parâmetros inicializa todos os campos para o valor padrão desse tipo, geralmente zero ou seu equivalente. Os construtores definidos para estruturas devem ter pelo menos um argumento para que não entrem em conflito com o construtor sem parâmetros.
Além disso, as estruturas geralmente têm campos criados usando a palavra-chave val
. As classes também podem ter esses campos. As estruturas e classes que têm campos definidos usando a palavra-chave val
também podem ser inicializadas em construtores adicionais, usando expressões de registro, conforme mostrado no código a seguir.
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)
Para mais informações, confira Campos Explícitos: A Palavra-Chave val
.
Como executar efeitos colaterais nos construtores
Um construtor primário em uma classe pode executar o código em uma associação do
. No entanto, e se você tiver que executar o código em um construtor adicional, sem uma associação do
? Para fazer isso, você deve usar a palavra-chave 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()
Os efeitos colaterais do construtor primário ainda são executados. Portanto, a saída é a seguinte:
Created a person object.
Created a person object.
Created an invalid person object.
O motivo pelo qual then
é necessário, em vez de outro do
, é que o significado padrão da palavra-chave do
é delimitar uma expressão unit
-returning, quando presente no corpo de um construtor adicional. Ela só tem um significado especial no contexto de construtores primários.
Auto-identificadores em construtores
Em outros membros, você fornecerá um nome para o objeto atual na definição de cada membro. Você também pode colocar o auto-identificador na primeira linha da definição de classe, usando a palavra-chave as
imediatamente após os parâmetros do construtor. O exemplo a seguir ilustra essa sintaxe.
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
Em construtores adicionais, você também pode definir um auto-identificador, colocando a cláusula as
logo após os parâmetros do construtor. O exemplo a seguir ilustra essa sintaxe:
type MyClass2(x : int) =
member this.X = x
new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X
Podem ocorrer problemas quando você tenta usar um objeto antes que ele seja totalmente definido. Portanto, o uso do auto-identificador pode fazer com que o compilador emita um aviso e insira verificações adicionais, para garantir que os membros de um objeto não sejam acessados antes que o objeto seja inicializado. Você deve usar o auto-identificador apenas nas associações do
do construtor primário ou após a palavra-chave then
em construtores adicionais.
O nome do auto-identificador não precisa ser this
. Pode ser qualquer identificador válido.
Atribuição de valores a propriedades na inicialização
Você pode atribuir valores às propriedades de um objeto de classe no código de inicialização, acrescentando uma lista de atribuições do formulário property = value
à lista de argumentos de um construtor. Isso é demonstrado no exemplo de código a seguir:
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)
A versão a seguir do código anterior ilustra a combinação de argumentos comuns, argumentos opcionais e configurações de propriedade em uma chamada de construtor:
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")
Construtores na classe herdada
Ao herdar de uma classe base que tenha um construtor, você deve especificar os argumentos na cláusula herdada. Para obter mais informações, confira Construtores e herança.
Construtores estáticos ou construtores de tipo
Além de especificar o código para a criação de objetos, as associações estáticas let
e do
podem ser criadas em tipos de classe, que são executados antes que o tipo seja usado pela primeira vez, para executar a inicialização no nível do tipo. Para obter mais informações, confira Associações let
em classes e Associações do
em classes.