Share via


Operador de sobrecarga (F#)

Este tópico descreve como sobrecarregar operadores aritméticos em uma classe ou um tipo de registro e no nível global.

// Overloading an operator as a class or record member. 
static member (operator-symbols) (parameter-list) = 
    method-body
// Overloading an operator at the global level
let [inline] (operator-symbols) parameter-list =
    function-body

Comentários

Na sintaxe anterior, o operator-symbol é uma das +, -, *, /, =e assim por diante. O parameter-list Especifica os operandos na ordem em que aparecem na sintaxe usual para o operador. O method-body constrói o valor resultante.

Sobrecargas de operador para operadores devem ser estáticas. Operador sobrecargas para operadores unários, como + e -, deve usar um til (~) na operator-symbol para indicar que o operador é um operador unário e não é um operador binário, como mostrado na seguinte declaração.

static member (~-) (v : Vector)

O código a seguir ilustra uma classe de vetor que tem apenas dois operadores, uma para unário negação e outra para multiplicação por um escalar. No exemplo, duas sobrecargas para multiplicação escalar são necessárias porque o operador deve funcionar independentemente da ordem em que o vetor e scalar aparecem.

type Vector(x: float, y : float) =
   member this.x = x
   member this.y = y
   static member (~-) (v : Vector) =
     Vector(-1.0 * v.x, -1.0 * v.y)
   static member (*) (v : Vector, a) =
     Vector(a * v.x, a * v.y)
   static member (*) (a, v: Vector) =
     Vector(a * v.x, a * v.y)
   override this.ToString() =
     this.x.ToString() + " " + this.y.ToString()

let v1 = Vector(1.0, 2.0)

let v2 = v1 * 2.0
let v3 = 2.0 * v1

let v4 = - v2

printfn "%s" (v1.ToString())
printfn "%s" (v2.ToString())
printfn "%s" (v3.ToString())
printfn "%s" (v4.ToString())

Criando novos operadores

Você pode sobrecarregar os operadores padrão, mas você também pode criar novos operadores de seqüências de determinados caracteres. Allowed operator characters are !, %, &, *, +, -, ., /, <, =, >, ?, @, ^, |, and ~. O ~ caractere tem um significado especial de fazer um operador unário e não faz parte da seqüência de caracteres de operador. Nem todos os operadores podem ser feitos unários, conforme é descrito em prefixo e operadores de Infix posteriormente neste tópico.

Dependendo da seqüência de caracteres exata que você usar, sua operadora terá determinados precedência e associatividade. Associatividade pode ser deixada tanto para a direita ou da direita para a esquerda e é usada sempre que os operadores do mesmo nível de prioridade aparecem na seqüência sem parênteses.

O caractere de operador . não afeta a precedência, para que, por exemplo, se você deseja definir sua própria versão de multiplicação, que tem a mesma precedência e associatividade como multiplicação comum, você pode criar operadores como .*.

Uma tabela que mostra a precedência de operadores de todos os F# pode ser encontrada em Símbolo e o referência de operador (F#).

Nomes de operador sobrecarregado

Quando o compilador F# compila uma expressão do operador, ele gera um método que possui um nome gerado pelo compilador para esse operador. Este é o nome que aparece na Microsoft intermediate language (MSIL) para o método e também na reflexão e IntelliSense. Normalmente, não é necessário usar esses nomes no código do F#.

A tabela a seguir mostra os operadores padrão e seus correspondentes nomes gerados.

Operador

Nome gerado

[]

op_Nil

::

op_Cons

+

op_Addition

-

op_Subtraction

*

op_Multiply

/

op_Division

@

op_Append

^

op_Concatenate

%

op_Modulus

&&&

op_BitwiseAnd

|||

op_BitwiseOr

^^^

op_ExclusiveOr

<<<

op_LeftShift

~~~

op_LogicalNot

>>>

op_RightShift

~+

op_UnaryPlus

~-

op_UnaryNegation

=

op_Equality

<=

op_LessThanOrEqual

>=

op_GreaterThanOrEqual

<

op_LessThan

>

op_GreaterThan

?

op_Dynamic

?<-

op_DynamicAssignment

|>

op_PipeRight

<|

op_PipeLeft

!

op_Dereference

>>

op_ComposeRight

<<

op_ComposeLeft

<@ @>

op_Quotation

<@@ @@>

op_QuotationUntyped

+=

op_AdditionAssignment

-=

op_SubtractionAssignment

*=

op_MultiplyAssignment

/=

op_DivisionAssignment

..

op_Range

.. ..

op_RangeStep

Outras combinações de caracteres de operador que não estão listadas aqui podem ser usadas como operadores e têm nomes que são compostos pela concatenação de nomes para os caracteres individuais da tabela a seguir. Por exemplo, +! torna-se op_PlusBang.

Caractere de operador

Nome

>

Greater

<

Less

+

Plus

-

Minus

*

Multiply

/

Divide

=

Equals

~

Twiddle

%

Percent

.

Dot

&

Amp

|

Bar

@

At

^

Hat

!

Bang

?

Qmark

(

LParen

,

Comma

)

RParen

[

LBrack

]

RBrack

Prefixo e nos operadores fixos

Prefixo operadores devem ser colocado na frente de um operando ou operandos, muito semelhante a uma função. Infix operadores devem ser colocados entre os dois operandos.

Apenas determinados operadores podem ser usados como operadores de prefixo. Alguns operadores são sempre os operadores de prefixo, outras pessoas podem ser fixos ou o prefixo e o restante são sempre operadores de infix. Os operadores de ! e ~, ou seqüências repetidas dessas, são sempre operadores de prefixo. Os operadores de +, -, +., -., &, &&, %, e %% pode ser a operadores de prefixo ou infix operadores. Distinguir a versão de prefixo desses operadores da versão fixos, adicionando um ~ no início de um operador de prefixo quando ele estiver definido. O ~ não é usado quando você usar o operador somente quando ela está definida.

Exemplo

O código a seguir ilustra o uso de sobrecarga de operador para implementar um tipo de fração. Uma fração é representada por um numerador e um denominador. A função hcf é usada para determinar o fator de máxima comum, que é usado para reduzir frações.

// Determine the highest common factor between
// two positive integers, a helper for reducing
// fractions.
let rec hcf a b =
  if a = 0u then b
  elif a<b then hcf a (b - a)
  else hcf (a - b) b

// type Fraction: represents a positive fraction
// (positive rational number).
type Fraction =
   {
      // n: Numerator of fraction.
      n : uint32
      // d: Denominator of fraction.
      d : uint32
   }

   // Produce a string representation. If the
   // denominator is "1", do not display it.
   override this.ToString() =
      if (this.d = 1u)
        then this.n.ToString()
        else this.n.ToString() + "/" + this.d.ToString()

   // Add two fractions.
   static member (+) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.d + f2.n * f1.d
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Adds a fraction and a positive integer.
   static member (+) (f1: Fraction, i : uint32) =
      let nTemp = f1.n + i * f1.d
      let dTemp = f1.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Adds a positive integer and a fraction.
   static member (+) (i : uint32, f2: Fraction) =
      let nTemp = f2.n + i * f2.d
      let dTemp = f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Subtract one fraction from another.
   static member (-) (f1 : Fraction, f2 : Fraction) =
      if (f2.n * f1.d > f1.n * f2.d)
        then failwith "This operation results in a negative number, which is not supported."
      let nTemp = f1.n * f2.d - f2.n * f1.d
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Multiply two fractions.
   static member (*) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.n
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Divide two fractions.
   static member (/) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.d
      let dTemp = f2.n * f1.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // A full set of operators can be quite lengthy. For example,
   // consider operators that support other integral data types,
   // with fractions, on the left side and the right side for each.
   // Also consider implementing unary operators.

let fraction1 = { n = 3u; d = 4u }
let fraction2 = { n = 1u; d = 2u }
let result1 = fraction1 + fraction2
let result2 = fraction1 - fraction2
let result3 = fraction1 * fraction2
let result4 = fraction1 / fraction2
let result5 = fraction1 + 1u
printfn "%s + %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result1.ToString())
printfn "%s - %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result2.ToString())
printfn "%s * %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result3.ToString())
printfn "%s / %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result4.ToString())
printfn "%s + 1 = %s" (fraction1.ToString()) (result5.ToString())
  

Operadores no nível Global

Você também pode definir os operadores no nível global. O código a seguir define um operador +?.

let inline (+?) (x: int) (y: int) = x + 2*y
printf "%d" (10 +? 1)

A saída do código acima é 12.

Você pode redefinir os operadores aritméticos regulares dessa maneira, porque as regras de escopo para F# determinam que os operadores recém-definido prevalecem sobre os operadores internos.

A palavra-chave inline é geralmente usado com operadores globais, que são geralmente pequenas funções que são melhor integradas o código de chamada. Inline de funções de operador de tomada de também permite trabalhar com os parâmetros de tipo resolvido estaticamente para produzir estaticamente resolvido código genérico. Para obter mais informações, consulte Funções embutidas (F#) e Resolvido estaticamente os parâmetros de tipo (F#).

Consulte também

Conceitos

Membros (F#)