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.
As extensões de tipo (também chamadas de aumentos) são uma família de recursos que permitem adicionar novos membros a um tipo de objeto definido anteriormente. Os três recursos são:
- Extensões de tipo intrínseco
- Extensões de tipo opcionais
- Métodos de extensão
Cada um pode ser usado em cenários diferentes e tem diferentes compensações.
Sintaxe
// Intrinsic and optional extensions
type typename with
member self-identifier.member-name =
body
...
// Extension methods
open System.Runtime.CompilerServices
[<Extension>]
type Extensions() =
[<Extension>]
static member extension-name (ty: typename, [args]) =
body
...
Extensões de tipo intrínseco
Uma extensão de tipo intrínseco é uma extensão de tipo que estende um tipo definido pelo usuário.
As extensões de tipo intrínseco devem ser definidas no mesmo arquivo e no mesmo namespace ou módulo que o tipo que estão estendendo. Qualquer outra definição resultará em extensões de tipo opcionais.
As extensões de tipo intrínseco às vezes são uma maneira mais limpa de separar a funcionalidade da declaração de tipo. O exemplo a seguir mostra como definir uma extensão de tipo intrínseco:
namespace Example
type Variant =
| Num of int
| Str of string
module Variant =
let print v =
match v with
| Num n -> printf "Num %d" n
| Str s -> printf "Str %s" s
// Add a member to Variant as an extension
type Variant with
member x.Print() = Variant.print x
O uso de uma extensão de tipo permite que você separe cada uma das seguintes opções:
- A declaração de um
Varianttipo - Funcionalidade para imprimir a
Variantclasse dependendo de sua "forma" - Uma maneira de acessar a funcionalidade de impressão com a notação de estilo
.de objeto
Essa é uma alternativa para definir tudo como um membro em Variant. Embora não seja uma abordagem inerentemente melhor, pode ser uma representação mais limpa da funcionalidade em algumas situações.
As extensões de tipo intrínseco são compiladas como membros do tipo que aumentam e aparecem no tipo quando o tipo é examinado por reflexão.
Extensões de tipo opcionais
Uma extensão de tipo opcional é uma extensão que aparece fora do módulo original, namespace ou assembly do tipo que está sendo estendido.
Extensões de tipo opcionais são úteis para estender um tipo que você mesmo não definiu. Por exemplo:
module Extensions
type IEnumerable<'T> with
/// Repeat each element of the sequence n times
member xs.RepeatElements(n: int) =
seq {
for x in xs do
for _ in 1 .. n -> x
}
Agora você pode acessar RepeatElements como se fosse um membro desde IEnumerable<T> que o Extensions módulo seja aberto no escopo em que você está trabalhando.
Extensões opcionais não aparecem no tipo estendido quando examinadas por reflexão. As extensões opcionais devem estar em módulos e só estarão no escopo quando o módulo que contém a extensão estiver aberto ou estiver no escopo.
Membros de extensão opcionais são compilados para membros estáticos para os quais a instância de objeto é passada implicitamente como o primeiro parâmetro. No entanto, eles atuam como se fossem membros de instância ou membros estáticos de acordo com a forma como são declarados.
Membros de extensão opcionais também não são visíveis para consumidores C# ou Visual Basic. Eles só podem ser consumidos em outro código F#.
Limitação genérica de extensões de tipo intrínseco e opcional
É possível declarar uma extensão de tipo em um tipo genérico em que a variável de tipo é restrita. O requisito é que a restrição da declaração de extensão corresponda à restrição do tipo declarado.
No entanto, mesmo quando as restrições são correspondidas entre um tipo declarado e uma extensão de tipo, é possível que uma restrição seja inferida pelo corpo de um membro estendido que impõe um requisito diferente sobre o parâmetro de tipo do que o tipo declarado. Por exemplo:
open System.Collections.Generic
// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
member this.Sum() = Seq.sum this
Não há como fazer com que esse código funcione com uma extensão de tipo opcional:
- Como é, o
Summembro tem uma restrição diferente em'T(static member get_Zeroestatic member (+)) do que a extensão de tipo define. - Modificar a extensão de tipo para ter a mesma restrição em
Sumque não corresponderá mais à restriçãoIEnumerable<'T>definida. - Alterar
member this.Sumparamember inline this.Sumfornecerá um erro de que as restrições de tipo são incompatíveis.
O que é desejado são métodos estáticos que "flutuam no espaço" e podem ser apresentados como se estivessem estendendo um tipo. É aí que os métodos de extensão se tornam necessários.
Métodos de extensão
Por fim, os métodos de extensão (às vezes chamados de "membros de extensão de estilo C#") podem ser declarados em F# como um método de membro estático em uma classe.
Os métodos de extensão são úteis para quando você deseja definir extensões em um tipo genérico que restringirá a variável de tipo. Por exemplo:
namespace Extensions
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
Quando usado, esse código fará com que ele apareça como se Sum estivesse definido IEnumerable<T>, desde Extensions que tenha sido aberto ou esteja no escopo.
Para que a extensão esteja disponível para VB.NET código, um extra ExtensionAttribute é necessário no nível do assembly:
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
Outras observações
As extensões de tipo também têm os seguintes atributos:
- Qualquer tipo que possa ser acessado pode ser estendido.
- Extensões de tipo intrínseco e opcional podem definir qualquer tipo de membro, não apenas métodos. Portanto, as propriedades de extensão também são possíveis, por exemplo.
- O
self-identifiertoken na sintaxe representa a instância do tipo que está sendo invocado, assim como os membros comuns. - Membros estendidos podem ser membros estáticos ou de instância.
- As variáveis de tipo em uma extensão de tipo devem corresponder às restrições do tipo declarado.
As seguintes limitações também existem para extensões de tipo:
- Extensões de tipo não dão suporte a métodos virtuais ou abstratos.
- As extensões de tipo não dão suporte a métodos de substituição como aumentos.
- As extensões de tipo não dão suporte a parâmetros de tipo resolvidos estaticamente.
- Extensões opcionais de tipo não dão suporte a construtores como aumentos.
- As extensões de tipo não podem ser definidas em abreviações de tipo.
- As extensões de tipo não são válidas (
byref<'T>embora possam ser declaradas). - Extensões de tipo não são válidas para atributos (embora possam ser declaradas).
- Você pode definir extensões que sobrecarregam outros métodos de mesmo nome, mas o compilador F# dá preferência aos métodos de não extensão se houver uma chamada ambígua.
Por fim, se existirem várias extensões de tipo intrínseco para um tipo, todos os membros deverão ser exclusivos. Para extensões de tipo opcionais, os membros em extensões de tipo diferentes para o mesmo tipo podem ter os mesmos nomes. Os erros de ambiguidade ocorrerão somente se o código do cliente abrir dois escopos diferentes que definem os mesmos nomes de membro.