Classes (F#)
Classes de são os tipos que representam os 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 ofereça suporte a programação orientada a objeto em F#.
Na sintaxe anterior, o type-name é qualquer identificador válido. O type-params descreve todos os parâmetros opcional tipo genérico. 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 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.
Especifique 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 o identificador de self, que pode ser usado na definição de tipo para se referir à instância do 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 Unidos recursiva tipos, que são os tipos que fazem referência entre si, juntamente com o and palavra-chave, assim como mutuamente recursiva funções são. 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 idiomas. Em uma classe de 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 siga. Os argumentos do construtor principal estão no escopo em toda a declaração de classe.
Você pode adicionar os 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 que 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 da classe é criada. Se um let a ligação é uma função, e 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, e em seguida, ele é compilado em uma variável que é local para o construtor. Caso contrário, ele é compilado em um campo da classe. O do que siga as expressões 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 do construtor é chamado.
Os 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 self, se houver.
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 Visual Basic. Você pode definir um identificador automático 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 de lista e, em seguida, especifique o nome do identificador.
Para definir um identificador de auto 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 self. Na primeira linha, o as palavra-chave é usada para definir o identificador de self. 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 de outros.NET idiomas, você pode nomear o identificador de auto-desejar; Você não está restrito aos nomes como self, Me, ou this.
O identificador de auto-é declarado com a as palavra-chave não é 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 de ligações.
Parâmetros de tipo genérico
Os 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 é 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 um. Em F#, apenas uma classe de 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 de 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 F# tipos além 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 segue:
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 orientado a objeto de tipo 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 o 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. Thought–out um único, também discriminated a união, com o padrão apropriado a correspondência de código, geralmente pode ser usado como uma alternativa mais simples para uma hierarquia de objetos. Para obter mais informações sobre 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 quando as demandas de um tipo excederem o que pode ser realizado com sua simplicidade. Os registros são agregados basicamente simples 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 registros, consulte Registros (F#).
Estruturas também são úteis para pequenos agregados de dados, mas diferem de classes e os registros em que eles estão.Tipos de valor líquido. Classes e os registros são.Tipos de referência NET. A semântica dos tipos de valor e tipos de referência é diferente tipos de valor são passados por valor. Isso significa que eles são copiados bit por bit, quando são passados como um parâmetro ou retornados de uma função. 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#).