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.
Este tópico descreve como sobrecarregar operadores aritméticos em uma classe ou tipo de registro e no nível global.
Sintaxe
// 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
Observações
Na sintaxe anterior, o símbolo do operador é um de +, , -, *, /e =assim por diante. A lista de parâmetros especifica os operandos na ordem em que aparecem na sintaxe usual desse operador. O corpo do método constrói o valor resultante.
As sobrecargas de operador para operadores devem ser estáticas. As sobrecargas de operador para operadores unários, como + e -, devem usar um bloco (~) no símbolo do operador para indicar que o operador é um operador unário e não um operador binário, conforme mostrado na declaração a seguir.
static member (~-) (v : Vector)
O código a seguir ilustra uma classe de vetor que tem apenas dois operadores, um para menos unário e outro 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 na qual o vetor e o escalar 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())
Saída:
1 2
2 4
2 4
-2 -4
Criando novos operadores
Você pode sobrecarregar todos os operadores padrão, mas também pode criar novos operadores a partir de sequências de determinados caracteres. Os caracteres de operador permitidos são !, , , $, %, &, *+, , -., /, <, , =, >, ?, @, , , ^e |. ~ O ~ caractere tem o significado especial de tornar um operador unário e não faz parte da sequência de caracteres do operador. Nem todos os operadores podem ser unários.
Dependendo da sequência de caracteres exata usada, o operador terá uma certa precedência e associatividade. A associatividade pode ser da esquerda para a direita ou direita para a esquerda e é usada sempre que operadores do mesmo nível de precedência aparecem em sequência sem parênteses.
O caractere . do operador não afeta a precedência, de modo que, por exemplo, se você quiser definir sua própria versão de multiplicação que tenha a mesma precedência e associatividade que a multiplicação comum, você poderá criar operadores como .*.
O $ operador deve ficar sozinho e sem símbolos adicionais.
Uma tabela que mostra a precedência de todos os operadores em F# pode ser encontrada na Referência de Símbolo e Operador.
Nomes de operador sobrecarregados
Quando o compilador F# compila uma expressão de operador, ele gera um método que tem um nome gerado pelo compilador para esse operador. Esse é o nome que aparece na CIL (linguagem intermediária comum) para o método e também em reflexão e IntelliSense. Normalmente, você não precisa usar esses nomes no código F#.
A tabela a seguir mostra os operadores padrão e seus nomes gerados correspondentes.
| 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 |
Observe que o not operador em F# não emite op_Inequality porque não é um operador simbólico. É uma função que emite IL que nega uma expressão booliana.
Outras combinações de caracteres de operador que não estão listados 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, +! se tornará op_PlusBang.
| Caractere do operador | Nome |
|---|---|
> |
Greater |
< |
Less |
+ |
Plus |
- |
Minus |
* |
Multiply |
/ |
Divide |
= |
Equals |
~ |
Twiddle |
$ |
Dollar |
% |
Percent |
. |
Dot |
& |
Amp |
| |
Bar |
@ |
At |
^ |
Hat |
! |
Bang |
? |
Qmark |
( |
LParen |
, |
Comma |
) |
RParen |
[ |
LBrack |
] |
RBrack |
: |
Colon |
O uso de : operadores personalizados é parcialmente reservado. Ele só pode ser usado em operadores em que o primeiro caractere está > ou . onde o primeiro caractere após qualquer número de líder . é > , por exemplo, >: ou .>:.
Operadores de prefixo e infixo
Espera-se que os operadores de prefixo sejam colocados na frente de um operando ou operandos, assim como uma função. Espera-se que os operadores de infixo sejam colocados entre os dois operandos.
Somente determinados operadores podem ser usados como operadores de prefixo. Alguns operadores são sempre operadores de prefixo, outros podem ser infixos ou prefixos, e o restante sempre são operadores de infixo. Operadores que começam com !, exceto !=, e o operador ~, ou sequências repetidas de , são sempre operadores de ~prefixo. Os operadores +, -, +., , -., &, &&, , %e %% podem ser operadores de prefixo ou operadores de infixo. Você distingue a versão de prefixo desses operadores da versão de infixo adicionando um ~ no início de um operador de prefixo quando ele for definido. O ~ não é usado quando você usa o operador, somente quando ele é definido.
Exemplo
O código a seguir ilustra o uso da sobrecarga do 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 comum mais alto, 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())
Saída:
3/4 + 1/2 = 5/4
3/4 - 1/2 = 1/4
3/4 * 1/2 = 3/8
3/4 / 1/2 = 3/2
3/4 + 1 = 7/4
Operadores no nível global
Você também pode definir 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 operadores recém-definidos têm precedência sobre os operadores internos.
A palavra-chave inline geralmente é usada com operadores globais, que geralmente são pequenas funções que são melhor integradas ao código de chamada. Tornar as funções de operador embutidas também permite que elas trabalhem com parâmetros de tipo estaticamente resolvidos para produzir código genérico estaticamente resolvido. Para obter mais informações, consulte funções embutidas e parâmetros de tipo resolvidos estaticamente.