Registros (F#)
Registros representam simples agregações de valores nomeados opcionalmente com membros.
[ attributes ]
type [accessibility-modifier] typename = {
[ mutable ] label1 : type1;
[ mutable ] label2 : type2;
...
}
member-list
Comentários
Na sintaxe anterior, typename é o nome do tipo de registro, label1 e label2 são nomes de valores, conhecidos como etiquetas, e type1 e type2 são os tipos desses valores.member-listé a lista opcional de membros do tipo.
Estes são alguns exemplos.
type Point = { x : float; y: float; z: float; }
type Customer = { First : string; Last: string; SSN: uint32; AccountNumber : uint32; }
Quando cada rótulo em uma linha separada, a vírgula é opcional.
Você pode definir valores em expressões, conhecidas como expressões de registro.O compilador infere o tipo de rótulos usada (se os rótulos são suficientemente diferentes daqueles de outros tipos de registro).Chaves ({}) coloque a expressão de registro.O código a seguir mostra uma expressão de registro inicializa um registro com três elementos float com rótulos x, y e z.
let mypoint = { x = 1.0; y = 1.0; z = -1.0; }
Não use a forma abreviada se pode haver outro tipo também tem 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 as do tipo declarado anteriormente, portanto, o exemplo anterior, mypoint3D é inferido para ser Point3D.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; }
Métodos podem ser definidos para tipos de registro como para tipos de classe.
Criação de registros usando expressões de registro
Você pode inicializar registros usando rótulos que são definidos no registro.Uma expressão que faz isso é conhecida como um expressão de registro.Use chaves para delimitar a expressão do registro e usar vírgula como um 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; }
A ponto e vírgula após o último campo na expressão do registro e na definição de tipo é opcional, independentemente dos campos estarem todas em uma linha.
Quando você cria um registro, você deve fornecer valores para cada campo.Você não pode referir-se os valores dos outros campos na expressão de inicialização para qualquer campo.
No código a seguir, o tipo de myRecord2 deduzido dos nomes dos campos.Opcionalmente, você pode especificar explicitamente o nome do tipo.
let myRecord2 = { MyRecord.X = 1; MyRecord.Y = 2; MyRecord.Z = 3 }
Outra forma de construção de registro pode ser útil quando você tem que copiar um registro existente e possivelmente alterar alguns dos valores de campo.Linha de código a seguir ilustra isso.
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Essa forma de expressão registro é chamada de copiar e atualizar registro expressão.
Registros são imutáveis por padrão; No entanto, você pode facilmente criar registros modificados usando uma expressão de cópia e atualização.Você 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 são inicializados com valores padrão e usar uma cópia e atualizar o registro de expressão para definir os campos que são diferentes 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 }
Padrão de correspondência de registros
Registros podem ser usados com correspondência de padrões.Você pode especificar explicitamente alguns campos e fornecer variáveis para outros campos que serão atribuídos quando ocorre 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 é como segue.
Point is at the origin.
Point is on the x-axis. Value is 100.000000.
Point is at (10.000000, 0.000000, -1.000000).
Diferenças entre Classes e registros
Campos de registros diferem das classes que elas são expostas como propriedades automaticamente e eles são usados na criação e cópia de registros.Construção de registro também difere da construção de 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.Classes não têm nenhuma relação direta entre os parâmetros do construtor, campos e propriedades.
Como os tipos de união e estrutura, registros possuem semântica de igualdade estrutural.Classes têm referência semântica de igualdade.O código a seguir mostra um exemplo disso:
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."
Se você escrever o mesmo código com classes, objetos de dois classe seria desiguais porque os dois valores representa dois objetos no heap e somente os endereços poderiam ser comparados (a menos que o tipo de classe substitui o System.Object.Equals método).