Extensões de tipo (F#)

Extensões de tipo permitem que você adicione novos membros para um tipo de objeto definido anteriormente.

// Intrinsic extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

// Optional extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

Comentários

Há duas formas de extensões de tipo que têm a sintaxe e o comportamento ligeiramente diferentes. Uma extensão intrínseca é uma extensão que aparece no mesmo namespace ou módulo, no mesmo arquivo de origem, e no mesmo assembly (DLL ou arquivo executável) que o tipo que está sendo estendido. Uma extensão opcional é uma extensão que aparece fora do módulo, namespace, ou assembly original do tipo que está sendo estendido. As extensões intrínsecas aparecem no tipo quando o tipo é examinado por reflexão, mas as extensões opcionais não. As extensões opcionais devem estar em módulos e ficam apenas no escopo quando o módulo que contém a extensão é aberto.

Na sintaxe anterior, typename representa o tipo que está sendo estendido. Qualquer tipo que pode ser acessado pode ser estendido, mas o nome de tipo deve ser um nome real de tipo, não uma abreviação de tipo. Você pode definir vários membros em uma extensão de tipo. O self-identifier representa a instância do objeto que está sendo invocado, assim como nos membros comuns.

A palavra-chave end é opcional na sintaxe leve.

Os membros definidos em extensões de tipo podem ser usados assim como outros membros em um tipo de classe. Como outros membros, eles podem ser estáticos ou membros da instância. Esses métodos também são conhecidos como métodos de extensão; as propriedades são conhecidas como propriedades de extensão, e assim por diante. Os membros opcionais de extensão são compilados a membros estáticos para que a instância de objeto seja passada implicitamente como primeiro parâmetro. No entanto, atuam como se fossem membros da instância ou membros estáticos de acordo com a forma como são declarados. Os membros de extensão implícitos estão incluídos como membros do tipo e podem ser usados sem restrição.

Os métodos de extensão não podem ser métodos virtuais ou abstratos. Eles podem sobrecarregar outros métodos do mesmo nome, mas o compilador fornece a preferência a métodos sem extensão no caso de uma chamada ambígua.

Se existirem várias extensões intrínsecas para um tipo, todos os membros deverão ser exclusivos. Para extensões de tipo opcionais, membros em diferentes extensões de tipo para o mesmo tipo poderão ter os mesmos nomes. Erros de ambiguidade surgem somente se o código do cliente abrir dois escopos diferentes que definem os mesmos nomes de membros.

No exemplo a seguir, um tipo em um módulo tem uma extensão intrínseca de tipo. Para o código do cliente fora do módulo, a extensão do tipo aparece como um membro comum do tipo em todos os aspectos.

module MyModule1 =

    // Define a type. 
    type MyClass() =
      member this.F() = 100

    // Define type extension. 
    type MyClass with 
       member this.G() = 200

module MyModule2 =
   let function1 (obj1: MyModule1.MyClass) =
      // Call an ordinary method.
      printfn "%d" (obj1.F())
      // Call the extension method.
      printfn "%d" (obj1.G())

Você pode usar extensões intrínsecas de tipo para separar a definição de um tipo em seções. Isso pode ser útil no gerenciamento de definições de tipo grande, por exemplo, para manter o código gerado pelo compilador e o código criado separados ou para agrupar o código criado por pessoas diferentes ou associado com a funcionalidade diferente.

No exemplo a seguir, uma extensão de tipo opcional estende o tipo System.Int32 com um método de extensão FromString que chama o membro estático Parse. O método testFromString demonstra que o novo membro é chamado assim como qualquer membro de instância.

// Define a new member method FromString on the type Int32. 
type System.Int32 with 
    member this.FromString( s : string ) =
       System.Int32.Parse(s)

let testFromString str =  
    let mutable i = 0
    // Use the extension method.
    i <- i.FromString(str)
    printfn "%d" i

testFromString "500"

O novo membro de instância aparecerá como qualquer outro método do tipo Int32 no IntelliSense, mas somente quando o módulo que contém a extensão for aberto ou estiver incluído de outra maneira no escopo.

Métodos de extensão genéricos

Antes do F# 3.1, o compilador do F# não oferecia suporte o uso de métodos de extensão do estilo C# com uma variável de tipo genérico, tipo de matriz, tipo de tuple, ou um tipo de função de F#, como o parâmetro "this". O F# 3.1 oferece suporte ao uso desses membros de extensão.

Por exemplo, em código do F# 3.1, você pode usar métodos de extensão com assinaturas que lembrem a seguinte sintaxe em C#:

static member Method<T>(this T input, T other)

Essa abordagem é útil principalmente quando o parâmetro de tipo genérico é restringido. Além disso, agora você pode declarar membros de extensão como esse no código F# e definir um conjunto adicional e semanticamente vasto de métodos de extensão. No F#, geralmente você define membros de extensão como o exemplo a seguir mostra:

type seq<’T> with
    /// Repeat each element of the sequence n times
    member xs.RepeatElements(n: int) =
        seq { for x in xs do for i in 1 .. n do yield x }

Entretanto, para um tipo genérico, a variável de tipo não pode ser restrita. Agora você pode declarar um membro de extensão de estilo C# em F# para contornar a limitação. Quando você combina esse tipo de declaração com o recurso interno de F#, você pode enfrentar algoritmos genéricos como membros de extensão.

Considere a seguinte declaração:

[<Extension>]
type ExtraCSharpStyleExtensionMethodsInFSharp () =
    [<Extension>]
    static member inline Sum(xs: seq<’T>) = Seq.sum xs

Usando essa declaração, você pode escrever um código parecido com o exemplo a seguir.

let listOfIntegers = [ 1 .. 100 ]
let listOfBigIntegers = [ 1I to 100I ]
let sum1 = listOfIntegers.Sum()
let sum2 = listOfBigIntegers.Sum()

Nesse código, o mesmo código aritmético genérico é aplicado às listas de dois tipos sem a sobrecarga, por meio da definição de um único membro de extensão.

Consulte também

Outros recursos

Referência da linguagem F#

Membros (F#)