Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os registros representam agregações simples de valores nomeados, opcionalmente com membros. Eles podem ser structs ou tipos de referência. Eles são tipos de referência por padrão.
Sintaxe
[ attributes ]
type [accessibility-modifier] typename =
[accessibility-modifier] {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
[ member-list ]
O accessibility modifier antes afeta typename a visibilidade de todo o tipo e é public por padrão. O segundo accessibility modifier afeta apenas o construtor e os campos.
Observações
Na sintaxe anterior, typename é o nome do tipo de registro, label1 e label2 são nomes de valores, conhecidos como rótulos, e type1 e type2 são os tipos desses valores.
member-list é a lista opcional de membros para o tipo. Você pode usar o [<Struct>] atributo para criar um registro struct em vez de um registro que é um tipo de referência.
Estes são alguns exemplos:
// Labels are separated by semicolons when defined on the same line.
type Point = { X: float; Y: float; Z: float }
// You can define labels on their own line with or without a semicolon.
type Customer =
{ First: string
Last: string
SSN: uint32
AccountNumber: uint32 }
// A struct record.
[<Struct>]
type StructPoint = { X: float; Y: float; Z: float }
Quando cada rótulo está em uma linha separada, o ponto e vírgula é opcional.
Você pode definir valores em expressões conhecidas como expressões de registro. O compilador infere o tipo dos rótulos usados (se os rótulos forem suficientemente distintos dos de outros tipos de registro). Chaves ({ }) incluem a expressão de registro. O código a seguir mostra uma expressão de registro que inicializa um registro com três elementos float com rótulos xy e z.
let mypoint = { X = 1.0; Y = 1.0; Z = -1.0 }
Não use o formulário abreviado se houver outro tipo que também tenha os mesmos rótulos.
type Point = { X: float; Y: float; Z: float }
type Point3D = { X: float; Y: float; Z: float }
// Ambiguity: Point or Point3D?
let mypoint3D = { X = 1.0; Y = 1.0; Z = 0.0 }
Os rótulos do tipo declarado mais recentemente têm precedência sobre os do tipo declarado anteriormente, portanto, no exemplo anterior, mypoint3D é inferido como Point3Dsendo . Você pode especificar explicitamente o tipo de registro, como no código a seguir.
let myPoint1 = { Point.X = 1.0; Y = 1.0; Z = 0.0 }
Os métodos podem ser definidos para tipos de registro, assim como para tipos de classe.
Criando registros usando expressões de registro
Você pode inicializar registros usando os rótulos definidos no registro. Uma expressão que faz isso é conhecida como uma expressão de registro. Use chaves para colocar a expressão de registro e usar o ponto-e-vírgula como delimitador.
O exemplo a seguir mostra como criar um registro.
type MyRecord = { X: int; Y: int; Z: int }
let myRecord1 = { X = 1; Y = 2; Z = 3 }
Os ponto e vírgula após o último campo na expressão de registro e na definição de tipo são opcionais, independentemente de todos os campos estarem em uma única linha.
Ao criar um registro, você deve fornecer valores para cada campo. Você não pode se referir aos valores de outros campos na expressão de inicialização para qualquer campo.
No código a seguir, o tipo de myRecord2 é inferido dos nomes dos campos. Opcionalmente, você pode especificar o nome do tipo explicitamente.
let myRecord2 =
{ MyRecord.X = 1
MyRecord.Y = 2
MyRecord.Z = 3 }
Outra forma de construção de registro pode ser útil quando você precisa copiar um registro existente e, possivelmente, alterar alguns dos valores de campo. A linha de código a seguir ilustra isso.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Essa forma da expressão de registro é chamada de expressão de registro de cópia e atualização.
Os registros são imutáveis por padrão; no entanto, você pode criar facilmente registros modificados usando uma expressão de cópia e atualização. Você também pode especificar explicitamente um campo mutável.
type Car =
{ Make: string
Model: string
mutable Odometer: int }
let myCar =
{ Make = "Fabrikam"
Model = "Coupe"
Odometer = 108112 }
myCar.Odometer <- myCar.Odometer + 21
Não use o atributo DefaultValue com campos de registro. Uma abordagem melhor é definir instâncias padrão de registros com campos inicializados para valores padrão e, em seguida, usar uma expressão de registro de cópia e atualização para definir todos os campos que diferem dos valores padrão.
// Rather than use [<DefaultValue>], define a default record.
type MyRecord =
{ Field1 : int
Field2 : int }
let defaultRecord1 = { Field1 = 0; Field2 = 0 }
let defaultRecord2 = { Field1 = 1; Field2 = 25 }
// Use the with keyword to populate only a few chosen fields
// and leave the rest with default values.
let rr3 = { defaultRecord1 with Field2 = 42 }
Criando registros mutuamente recursivos
Em algum momento, ao criar um registro, talvez você queira que ele dependa de outro tipo que você gostaria de definir posteriormente. Esse é um erro de compilação, a menos que você defina os tipos de registro para serem mutuamente recursivos.
A definição de registros mutuamente recursivos é feita com a and palavra-chave. Isso permite que você vincule dois ou mais tipos de registro juntos.
Por exemplo, o código a seguir define um tipo e um PersonAddress tipo como mutuamente recursivo:
// Create a Person type and use the Address type that is not defined
type Person =
{ Name: string
Age: int
Address: Address }
// Define the Address type which is used in the Person record
and Address =
{ Line1: string
Line2: string
PostCode: string
Occupant: Person }
Para criar instâncias de ambos, faça o seguinte:
// Create a Person type and use the Address type that is not defined
let rec person =
{
Name = "Person name"
Age = 12
Address =
{
Line1 = "line 1"
Line2 = "line 2"
PostCode = "abc123"
Occupant = person
}
}
Se você definisse o exemplo anterior sem a and palavra-chave, ele não seria compilado. A and palavra-chave é necessária para definições mutuamente recursivas.
Correspondência de padrões com registros
Os registros podem ser usados com correspondência de padrões. Você pode especificar alguns campos explicitamente e fornecer variáveis para outros campos que serão atribuídos quando ocorrer uma correspondência. O exemplo de código a seguir ilustra isso.
type Point3D = { X: float; Y: float; Z: float }
let evaluatePoint (point: Point3D) =
match point with
| { X = 0.0; Y = 0.0; Z = 0.0 } -> printfn "Point is at the origin."
| { X = xVal; Y = 0.0; Z = 0.0 } -> printfn "Point is on the x-axis. Value is %f." xVal
| { X = 0.0; Y = yVal; Z = 0.0 } -> printfn "Point is on the y-axis. Value is %f." yVal
| { X = 0.0; Y = 0.0; Z = zVal } -> printfn "Point is on the z-axis. Value is %f." zVal
| { X = xVal; Y = yVal; Z = zVal } -> printfn "Point is at (%f, %f, %f)." xVal yVal zVal
evaluatePoint { X = 0.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 100.0; Y = 0.0; Z = 0.0 }
evaluatePoint { X = 10.0; Y = 0.0; Z = -1.0 }
A saída desse código é a seguinte.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Registros e membros
Você pode especificar membros em registros como você pode com classes. Não há suporte para campos. Uma abordagem comum é definir um Default membro estático para facilitar a construção de registros:
type Person =
{ Name: string
Age: int
Address: string }
static member Default =
{ Name = "Phillip"
Age = 12
Address = "123 happy fun street" }
let defaultPerson = Person.Default
Se você usar um auto-identificador, esse identificador se referirá à instância do registro cujo membro é chamado:
type Person =
{ Name: string
Age: int
Address: string }
member this.WeirdToString() =
this.Name + this.Address + string this.Age
let p = { Name = "a"; Age = 12; Address = "abc123" }
let weirdString = p.WeirdToString()
Modificadores de acessibilidade em registros
O código a seguir ilustra o uso de modificadores de acessibilidade. Há três arquivos no projeto: Module1.fs, Test1.fse Test2.fs. Um tipo de registro interno e um tipo de registro com um construtor privado são definidos no Módulo1.
// Module1.fs
module Module1
type internal internalRecd = { X: int }
type recdWithInternalCtor = private { Y: int }
Test1.fs No arquivo, o registro interno deve ser inicializado com o internal modificador de acesso, isso porque o nível de proteção do valor e do registro deve corresponder e ambos devem pertencer ao mesmo assembly.
// Test1.fs
module Test1
open Module1
let myInternalRecd1 = { X = 2 } // This line will cause a compiler error.
let internal myInternalRecd2 = { X = 4 } // This is OK
Test2.fs No arquivo, o registro com o construtor privado não pode ser inicializado diretamente devido ao nível de proteção do construtor.
// Test2.fs
module Test2
open Module1
let myRecdWithInternalCtor = { Y = 6 } // This line will cause a compiler error.
Para obter mais informações sobre modificadores de acessibilidade, consulte o artigo controle de acesso .
Diferenças entre registros e classes
Os campos de registro diferem dos campos de classe, pois são expostos automaticamente como propriedades e são usados na criação e cópia de registros. A construção de registros também difere da construção da classe. Em um tipo de registro, você não pode definir um construtor. Em vez disso, a sintaxe de construção descrita neste tópico se aplica. As classes não têm relação direta entre parâmetros, campos e propriedades do construtor.
Como tipos de união e estrutura, os registros têm semântica de igualdade estrutural. As classes têm semântica de igualdade de referência. O exemplo de código a seguir demonstra isso.
type RecordTest = { X: int; Y: int }
let record1 = { X = 1; Y = 2 }
let record2 = { X = 1; Y = 2 }
if (record1 = record2) then
printfn "The records are equal."
else
printfn "The records are unequal."
A saída desse código é a seguinte:
The records are equal.
Se você escrever o mesmo código com classes, os dois objetos de classe serão diferentes porque os dois valores representariam dois objetos no heap e apenas os endereços seriam comparados (a menos que o tipo de classe substitua o System.Object.Equals método).
Se você precisar de igualdade de referência para registros, adicione o atributo [<ReferenceEquality>] acima do registro.