Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Thema wird beschrieben, wie arithmetische Operatoren in einer Klasse oder einem Datensatztyp und auf globaler Ebene überladen werden.
Syntax
// 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
Bemerkungen
In der vorherigen Syntax ist das Operatorsymbol eines von +, -, , *, /, =usw. Die Parameterliste gibt die Operanden in der Reihenfolge an, in der sie in der üblichen Syntax für diesen Operator angezeigt werden. Der Methodentext erstellt den resultierenden Wert.
Operatorüberladungen für Operatoren müssen statisch sein. Operatorüberladungen für unäre Operatoren, z + . B. und -, müssen eine Tilde (~) im Operatorsymbol verwenden, um anzugeben, dass der Operator ein unärer Operator und kein binärer Operator ist, wie in der folgenden Deklaration dargestellt.
static member (~-) (v : Vector)
Der folgende Code veranschaulicht eine Vektorklasse mit nur zwei Operatoren, eine für unär minus und eine für die Multiplikation durch einen Skalar. Im Beispiel sind zwei Überladungen für skalare Multiplikation erforderlich, da der Operator unabhängig von der Reihenfolge funktionieren muss, in der der Vektor und der Skalar angezeigt werden.
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())
Ausgabe:
1 2
2 4
2 4
-2 -4
Erstellen neuer Operatoren
Sie können alle Standardoperatoren überladen, aber Sie können auch neue Operatoren aus Sequenzen bestimmter Zeichen erstellen. Zulässige Operatorzeichen sind !, $, , %&, *+-, , ./<=, >?@^, , und . |~ Das ~ Zeichen hat die besondere Bedeutung, einen Operator unär zu machen und ist nicht Teil der Operator-Zeichensequenz. Nicht alle Operatoren können unär gemacht werden.
Abhängig von der genauen Zeichenfolge, die Sie verwenden, hat ihr Operator eine bestimmte Rangfolge und Zuordnung. Associativity kann entweder von links nach rechts oder von rechts nach links verwendet werden und wird verwendet, wenn Operatoren derselben Rangebene ohne Klammern in Sequenz angezeigt werden.
Das Operatorzeichen . wirkt sich nicht auf die Rangfolge aus, sodass Sie z. B. eine eigene Version der Multiplikation definieren möchten, die die gleiche Rangfolge und Zuordnung wie gewöhnliche Multiplikation aufweist, können Sie Operatoren wie .*z. B. erstellen.
Der $ Operator muss eigenständig und ohne zusätzliche Symbole stehen.
Eine Tabelle, die die Rangfolge aller Operatoren in F# zeigt, finden Sie in der Symbol- und Operatorreferenz.
Überladene Operatornamen
Wenn der F#-Compiler einen Operatorausdruck kompiliert, generiert er eine Methode mit einem compilergenerierten Namen für diesen Operator. Dies ist der Name, der in der gemeinsamen Zwischensprache (CIL) für die Methode und auch in Spiegelung und IntelliSense angezeigt wird. Normalerweise müssen Sie diese Namen nicht im F#-Code verwenden.
In der folgenden Tabelle sind die Standardoperatoren und die entsprechenden generierten Namen aufgeführt.
| Bediener | Generierter Name |
|---|---|
[] |
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 |
Beachten Sie, dass der not Operator in F# nicht ausgegeben op_Inequality wird, da er kein symbolischer Operator ist. Es handelt sich um eine Funktion, die IL ausgibt, die einen booleschen Ausdruck negiert.
Andere Kombinationen von Operatorzeichen, die hier nicht aufgeführt sind, können als Operatoren verwendet werden und namen aufweisen, die durch Verketten von Namen für die einzelnen Zeichen aus der folgenden Tabelle bestehen. Beispielsweise wird +! zu op_PlusBang.
| Operatorzeichen | Name |
|---|---|
> |
Greater |
< |
Less |
+ |
Plus |
- |
Minus |
* |
Multiply |
/ |
Divide |
= |
Equals |
~ |
Twiddle |
$ |
Dollar |
% |
Percent |
. |
Dot |
& |
Amp |
| |
Bar |
@ |
At |
^ |
Hat |
! |
Bang |
? |
Qmark |
( |
LParen |
, |
Comma |
) |
RParen |
[ |
LBrack |
] |
RBrack |
: |
Colon |
Die Verwendung in : benutzerdefinierten Operatoren ist teilweise reserviert. Es darf nur in Operatoren verwendet werden, bei denen das erste Zeichen ist > oder . wenn das erste Zeichen nach einer beliebigen Anzahl von Vorzeichen .> z. B. >: oder .>:.
Präfix- und Infixoperatoren
Präfixoperatoren werden voraussichtlich vor einem Operanden oder Operanden platziert, ähnlich wie eine Funktion. Infix-Operatoren werden erwartet, dass sie zwischen den beiden Operanden platziert werden.
Nur bestimmte Operatoren können als Präfixoperatoren verwendet werden. Einige Operatoren sind immer Präfixoperatoren, andere können infix oder präfix sein, und der Rest sind immer Infixoperatoren. Operatoren, die mit !, außer !=, und dem Operator ~, oder wiederholten Sequenzen von ~, beginnen, sind immer Präfixoperatoren. Die Operatoren +, , -, +., -., &, &&%und %% können Präfixoperatoren oder Infixoperatoren sein. Sie unterscheiden die Präfixversion dieser Operatoren von der Infix-Version, indem Sie beim Definieren eines Präfixoperators einen ~ Am Anfang eines Präfixoperators hinzufügen. Dies ~ wird nicht verwendet, wenn Sie den Operator verwenden, nur wenn er definiert ist.
Beispiel
Der folgende Code veranschaulicht die Verwendung von Operatorüberladungen zum Implementieren eines Bruchtyps. Ein Bruch wird durch einen Zähler und einen Nenner dargestellt. Die Funktion hcf wird verwendet, um den höchsten gemeinsamen Faktor zu bestimmen, der zum Reduzieren von Bruchzahlen verwendet wird.
// 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())
Ausgabe:
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
Operatoren auf globaler Ebene
Sie können operatoren auch auf globaler Ebene definieren. Der folgende Code definiert einen Operator +?.
let inline (+?) (x: int) (y: int) = x + 2*y
printf "%d" (10 +? 1)
Die Ausgabe des obigen Codes lautet 12.
Sie können die regulären arithmetischen Operatoren auf diese Weise neu definieren, da die Bereichsregeln für F# diktieren, dass neu definierte Operatoren Vorrang vor den integrierten Operatoren haben.
Das Schlüsselwort inline wird häufig mit globalen Operatoren verwendet, die häufig kleine Funktionen sind, die am besten in den aufrufenden Code integriert sind. Durch das Inlineieren von Operatorfunktionen können sie auch mit statisch aufgelösten Typparametern arbeiten, um statisch aufgelösten generischen Code zu erzeugen. Weitere Informationen finden Sie unter "Inlinefunktionen" und "Statisch aufgelöste Typparameter".