Classes (F#)
Classes de são os tipos que representam objetos que podem ter propriedades, métodos e eventos.
// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
Comentários
Classes representam a descrição fundamental do.Tipos de objeto de rede; a classe é o conceito de tipo primário que dá suporte a programação orientada a objeto em F#.
Na sintaxe anterior, o type-name é qualquer identificador válido. O type-params descreve os parâmetros de tipo genérico opcional. Ele consiste em nomes de parâmetro de tipo e restrições colocadas entre colchetes angulares (< e >). Para obter mais informações, consulte Genéricos (F#) e Restrições (F#). O parameter-list descreve os parâmetros do construtor. O modificador de acesso a primeiro diz respeito ao tipo; a segunda diz respeito ao construtor primário. Em ambos os casos, o padrão é public.
Você especifica a classe base para uma classe usando o inherit palavra-chave. Você deve fornecer argumentos, entre parênteses, para o construtor de classe base.
Você declarar campos ou função de valores que são locais para a classe usando let ligações e você deve seguir as regras gerais para let ligações. O do-bindings seção inclui o código a ser executada durante a construção do objeto.
O member-list consiste em construtores adicionais, instância e declarações de método estático, declarações de interface, ligações abstratas e declarações de propriedade e evento. Eles são descritos na Membros (F#).
O identifier que é usado com o opcional as palavra-chave dá um nome para a variável de instância ou auto-identificador, o que pode ser usado para se referir à instância do tipo na definição de tipo. Para obter mais informações, consulte a seção Self identificadores mais adiante neste tópico.
As palavras-chave class e end que marca o início e final da definição são opcionais.
Mutuamente recursiva tipos, que são os tipos que fazem referência entre si, fazem parte junto com o and exatamente como são mutuamente de recursivas: funções de palavra-chave. Por exemplo, consulte a seção tipos de recursiva mutuamente.
Construtores
O construtor é o código que cria uma instância do tipo de classe. Construtores de classes funcionam de modo um pouco diferente em F# do que em outros.NET diferentes. Em uma classe F#, sempre há um construtor primário, cujos argumentos são descritos na parameter-list que segue o nome de tipo e cujo corpo consiste de let (e let rec) ligações no início da declaração da classe e o do ligações que se seguem. Os argumentos do construtor principal estão no escopo em toda a declaração de classe.
Você pode adicionar construtores adicionais usando o new palavra-chave para adicionar um membro, da seguinte maneira:
new(argument-list) = constructor-body
O corpo do novo construtor deve chamar o construtor primário que está especificado na parte superior da declaração da classe.
O exemplo a seguir ilustra esse conceito. No código a seguir, MyClass tem dois construtores, um construtor primário leva dois argumentos e outro construtor que leva sem argumentos.
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
Let e fazer ligações
O let e do ligações em uma definição de classe formam o corpo do construtor da classe principal e, portanto, eles são executados sempre que uma instância de classe é criada. Se um let a ligação é uma função, em seguida, ele é compilado em um membro. Se a let a ligação é um valor que não é usado em qualquer função ou um membro, em seguida, ele é compilado em uma variável local para o construtor. Caso contrário, ele é compilado em um campo da classe. O do expressões que se seguem são compiladas no construtor primário e executar o código de inicialização para cada instância. Porque os construtores adicionais sempre chamar o construtor primário, o let ligações e do ligações sempre executado independentemente de qual é chamada de construtor.
Campos que são criados por let ligações podem ser acessadas em todo os métodos e propriedades da classe; No entanto, eles não podem ser acessados a partir de métodos estáticos, mesmo se os métodos estáticos levar a uma variável de instância como um parâmetro. Eles não podem ser acessados usando o identificador de si mesmo, caso exista.
Auto-identificadores
A identificador de auto- é um nome que representa a instância atual. Identificadores de auto-semelhante a this palavra-chave em C# ou C++ ou Me em Visual Basic. Você pode definir um identificador de auto-de duas maneiras diferentes, dependendo se você deseja que o auto-identificador do escopo para a definição de classe inteira ou apenas para um método individual.
Para definir um identificador de auto para toda a turma, use o as palavra-chave após os parênteses de fechamento do parâmetro de construtor lista e, em seguida, especifique o nome do identificador.
Para definir um identificador self para apenas um método, fornece o identificador de auto-na declaração de membro, pouco antes do nome do método e um ponto (.) como separador.
O exemplo de código a seguir ilustra as duas maneiras para criar um identificador de si mesmo. Na primeira linha, o as palavra-chave é usada para definir o identificador de si mesmo. Na quinta linha, o identificador this é usado para definir um identificador de auto-cujo escopo é restrito para o método PrintMessage.
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
Ao contrário do outro.NET diferentes, você pode nomear o identificador de auto-desejar; Você não está restrito a nomes como self, Me, ou this.
O identificador de auto-que é declarado com o as palavra-chave não foi inicializado até após o let as ligações são executadas. Portanto, ele não pode ser usado o let ligações. Você pode usar o identificador de auto-na do seção vinculações.
Parâmetros de tipo genérico
Parâmetros de tipo genérico são especificados em colchetes angulares (< e >), na forma de aspas simples seguida por um identificador. Vários parâmetros de tipo genérico são separados por vírgulas. O parâmetro de tipo genérico está no escopo em toda a declaração. O exemplo de código a seguir mostra como especificar parâmetros de tipo genérico.
type MyGenericClass<'a> (x: 'a) =
do printfn "%A" x
Argumentos de tipo são inferidos quando o tipo é usado. No código a seguir, o tipo inferido é uma seqüência de tuplas.
let g1 = MyGenericClass( seq { for i in 1 .. 10 -> (i, i*i) } )
Especificando a herança
O inherit cláusula identifica a classe base direta, se houver uma. No F#, somente uma classe base direta é permitida. Interfaces que uma classe implementa não são consideradas classes base. Interfaces são discutidos com o Interfaces (F#) tópico.
Você pode acessar os métodos e propriedades da classe base da classe derivada, usando a palavra-chave do idioma base como um identificador, seguido por um ponto (.) e o nome do membro.
Para obter mais informações, consulte Herança (F#).
Seção de membros
Você pode definir estático ou métodos de instância, propriedades, implementações de interface, membros abstratos, declarações de evento e construtores adicionais nesta seção. Let e fazer ligações não pode aparecer nesta seção. Porque os membros podem ser adicionados a uma variedade de tipos de F# com classes, eles são discutidos em um tópico separado, Membros (F#).
Tipos de recursiva mutuamente
Quando você define os tipos que fazem referência entre si de maneira circular, é enfileirar as definições de tipo usando o and palavra-chave. O and palavra-chave substitui a type palavra-chave em todos, exceto a primeira definição, como a seguir.
open System.IO
type Folder(pathIn: string) =
let path = pathIn
let filenameArray : string array = Directory.GetFiles(path)
member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray
and File(filename: string, containingFolder: Folder) =
member this.Name = filename
member this.ContainingFolder = containingFolder
let folder1 = new Folder(".")
for file in folder1.FileArray do
printfn "%s" file.Name
A saída é uma lista de todos os arquivos no diretório atual.
Quando usar Classes, Unions, registros e estruturas
Dada a variedade de tipos para escolher, você precisa ter um bom entendimento de quais cada tipo destina-se para selecionar o tipo apropriado para uma determinada situação. Classes são projetados para uso em contextos de programação orientada a objeto. Programação orientada a objeto é o paradigma dominante usado em aplicativos que são escritos para o.NET Framework. Se o seu código F# tem que trabalham em conjunto com o.NET Framework ou outra biblioteca orientada a objeto e, especialmente se você tiver que estendem a partir de um sistema de tipo orientada a objeto como, por exemplo, uma biblioteca de interface do usuário, as classes são provavelmente apropriados.
Se você não está interoperando intimamente com código orientado a objeto, ou se você estiver escrevendo código que é independente e, portanto, protegido contra interações freqüentes com código orientado a objeto, você deve considerar o uso de registros e uniões de discriminated. Um thought–out único, também discriminated união, juntamente com o padrão apropriado a correspondência de código, muitas vezes pode ser usado como uma alternativa mais simples para uma hierarquia de objetos. Para obter mais informações sobre as uniões discriminadas, consulte Uniões discriminadas (F#).
Registros têm a vantagem de ser mais simples do que as classes, mas os registros não são apropriados que excederem as demandas de um tipo que pode ser realizado com sua simplicidade. Os registros são basicamente simples agregados de valores, sem construtores separados que podem executar ações personalizadas, sem campos ocultos e sem as implementações de herança ou interface. Embora os membros como, por exemplo, propriedades e métodos podem ser adicionados aos registros para tornar seus comportamentos mais complexos, os campos armazenados em um registro ainda são uma agregação simple de valores. Para obter mais informações sobre os registros, consulte Registros (F#).
Estruturas também são úteis para pequenos agregados de dados, mas eles diferem das classes e registros, pois são.Tipos de valor líquido. Classes e os registros são.Tipos de referência NET. A semântica dos tipos de referência e tipos de valor é diferente tipos de valor são passados por valor. Isso significa que eles sejam copiados bit por bit, quando são retornados de uma função ou passados como um parâmetro. Eles também armazenados na pilha ou, se forem usados como um campo, incorporado dentro do objeto pai, em vez de armazenada em seu próprio local separado no heap. Portanto, as estruturas são apropriadas para dados acessados com freqüência, quando a sobrecarga de acessar a pilha é um problema. Para obter mais informações sobre estruturas, consulte Estruturas (F#).