ein Projekt von goloroden.de

Operatoren

Was sind Operatoren?

Nachdem Felder und Variablen nun bekannt sind, können Anwendungen Daten speichern und auch wieder abrufen. Allerdings fehlt noch eine Möglichkeit, Daten zu verändern, um beispielsweise Berechnungen ausführen oder Daten miteinander verknüpfen zu können. Die Änderung von Daten wird in C# durch sogenannte Operatoren unterstützt, deren einfachster der bereits bekannte Zuweisungsoperator = ist.

Arithmetische Operatoren

Den ersten Typ von Operatoren stellen in C# die arithmetischen Operatoren dar, die zum Rechnen mit Daten dienen. Arithmetische Operatoren können mit allen Wertetypen verwendet werden, die Zahlen darstellen, wobei darauf geachtet werden muss, dass die beiden miteinander zu verrechnenden Werte den gleichen Typ aufweisen.

Operator Funktion Beispiel
+ Addition
C#
1
2
3
4
5
int x = 2;
int y = 3;

// The sum is 5.
int sum = x + y;
- Subtraktion
C#
1
2
3
4
5
int x = 2;
int y = 3;

// The difference is -1.
int difference = x - y;
* Multiplikation
C#
1
2
3
4
5
int x = 2;
int y = 3;

// The product is 6.
int product = x * y;
/ Division
C#
1
2
3
4
5
int x = 2;
int y = 3;

// The quotient is 0.
int quotient = x / y;
% Modulo
C#
1
2
3
4
5
int x = 2;
int y = 3;

// The remainder is 2.
int remainder = x % y;

C# rechnet dabei nach den üblichen mathematischen Regeln, das heißt, es gilt Punkt- vor Strichrechnung. Allerdings kann diese Regelung - wie in der Mathematik auch - durch das Setzen von Klammern geändert werden.
C#
1
2
3
4
5
6
7
int x = 2;
int y = 3;
int z = 5;

// The result without brackets is 17, with brackets it is 25.
int resultA = x + y * z;
int resultB = (x + y) * z;
Während sich die Operatoren +, - und * so verhalten, wie man es erwarten würde, gibt es bei den beiden Divisionsoperatoren / und % einige Sonderfälle zu beachten. Zunächst ist das Ergebnis einer Verknüpfung von zwei Operanden mit einem arithmetischen Operator wieder vom gleichen Typ wie die beiden Operanden.

Wenn allerdings zwei Operanden von einem ganzzahligen Typ wie beispielsweise int oder long dividiert werden sollen, ist das Ergebnis unter Umständen nicht ganzzahlig. Deshalb werden in diesem Fall die Nachkommastellen abgeschnitten und nur der ganzzahlige Anteil als Ergebnis zurückgegeben.
C#
1
2
3
4
5
6
7
8
9
10
11
int x = 4;
int y = 2;

// The quotient is 2, since the result is an integer (2).
int quotient = x / y;

x = 3;

// The quotient is 1, since the result is not an integer (1,5) and
// hence the decimal part is cut off.
quotient = x / y;
Außerdem muss darauf geachtet werden, dass bei der Division nicht durch die Zahl Null geteilt wird, da dies mathematisch nicht definiert ist und zur Laufzeit der Anwendung ein entsprechender Fehler ausgelöst wird.

Die einzige Ausnahme von der Regel, dass arithmetische Operatoren mit jedem Wertetyp verwendet werden können, der Zahlen darstellt, ist der Modulo-Operator %, der nur für ganzzahlige Operanden definiert ist. Der Modulo-Operator gibt den Rest zurück, der entsteht, wenn der eine Operand durch den anderen geteilt wird.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int x = 1;
int y = 3;

// The remainder is 1, since 3 is not contained in 1, so there
// is 1 left.
int remainder = x % y;

x = 2;

// The remainder is 2, since 3 is not contained in 2, so there
// is 2 left.
remainder = x % y;

x = 3;

// The remainder is 0, since 3 is contained one time in 3, so there
// is 0 left.
remainder = x % y;
Die Modulo-Division entspricht also in gewisser Weise der Art, wie Uhrzeiten berechnet werden. Eine Uhr könnte die Stunden intern nämlich fortlaufend zählen, diese für die Ausgabe allerdings modulo zwölf rechnen.

Häufig kommt es vor, dass eine Variable mit sich selbst verrechnet wird, indem ihr Wert beispielsweise verdoppelt werden soll. Um den Wert einer Variablen i zu verdoppeln, muss dieser mit zwei multipliziert und das Ergebnis anschließend wieder der Variablen i zugewiesen werden.
C#
1
i = i * 2;
Da Berechnungen dieser Art häufig auftreten, gibt es eine kürzere Schreibweise für diesen Fall, bei dem eine Nennung der Variablen entfallen kann, und der Zuweisungsoperator seinen Platz mit dem arithmetischen Operator tauscht, wobei dieses Verfahren mit jedem arithmetischen Operator funktioniert.
C#
1
2
// Adequate to i = i * 2.
i *= 2;
Für die besonders häufig auftretenden Fälle, dass eine Variable um eins erhöht oder vermindert werden muss, gibt es sogar eine noch kürzere Schreibweise, indem die Variable mit dem Operator ++ oder -- verknüpft wird. Da dieser Operator keinen zweiten Operanden benötigt, wird er als unärer Operator bezeichnet, wohingegen die anderen arithmetischen Operatoren binäre Operatoren sind.
C#
1
2
// Adequate to i = i + 1.
i++;
Für die beiden unären Operatoren ++ und -- gibt es allerdings zwei Varianten - der Operator kann nämlich entweder hinter der Variablen, in der sogenannten Postfix-Notation, oder vor der Variablen, in der sogenannten Präfix-Notation angegeben werden. Der Unterschied liegt darin, ob zuerst der Wert verändert oder zuerst das Ergebnis zurückgegeben wird.
C#
1
2
3
4
5
6
7
8
int i = 3;

// Prints 3 to the console and increments i afterwards to 4.
Console.WriteLine(i++);

// Prints 5 to the console, since i is incremented before it
// gets printed.
Console.WriteLine(++i);
Schließlich gibt es noch zwei spezielle Fälle, die berücksichtigt werden müssen: Der mathematische Über- beziehungsweise Unterlauf. Ein Überlauf tritt immer dann auf, wenn das Resultat einer Berechnung zu groß für den entsprechenden Typ ist, ein Unterlauf analog dazu, wenn das Resultat zu klein ist.

Sofern diese beiden Fälle nicht gesondert berücksichtigt werden, treten Rechenfehler auf, sobald der größt- oder kleinstmögliche Wert über- oder unterschritten wurden. Die Anwendung wird ansonsten aber weiterhin ausgeführt. Falls eine explizite Überprüfung erforderlich ist, kann diese mit dem Schlüsselwort checked für einen abgeschlossenen Codeabschnitt aktiviert werden.
C#
1
2
3
4
5
6
7
8
int x = 2;
int y = 3;

// Activate checked calculations.
checked
{
    Console.WriteLine(x + y);
}
Im Fall eines Über- oder Unterlaufs tritt wie bei einer Division durch Null ein Fehler auf. So nützlich der Einsatz von checked ist, so sollte dennoch berücksichtigt werden, dass diese Prüfung Rechenzeit erfordert und die Anwendung daher an den zu prüfenden Stellen verlangsamt, und dass diese Prüfung einen Spezialfall prüft, der in der Praxis nicht all zu häufig auftritt. Ob checked verwendet wird oder nicht, hängt also vom konkreten Bedarf ab.

Sofern eine Anwendung generell als checked ausgeführt werden soll, kann dem Compiler dies durch den Parameter /checked mitgeteilt werden. Auf diese Art ist es nicht notwendig, alle Stellen innerhalb des Codes mit dem Schlüsselwort checked zu kennzeichnen. Allerdings ist es möglich, einzelne Stellen innerhalb des Codes mit dem Schlüsselwort unchecked zu kennzeichnen, um sie von der generellen Prüfung auszuschließen, wobei dieses Schlüsselwort genauso verwendet wird wie checked.
C#
1
2
3
4
5
6
7
8
int x = 2;
int y = 3;

// Activate unchecked calculations.
unchecked
{
    Console.WriteLine(x + y);
}

Relationale Operatoren

Im Gegensatz zu arithmetischen Operatoren dienen die relationalen Operatoren dazu, etwas über das Verhältnis zweier Operanden auszusagen. Mit ihnen kann geprüft werden, ob die beiden Operanden gleich, ungleich, größer, kleiner, größer gleich oder kleiner gleich sind. Als Resultat wird immer ein Wahrheitswert zurückgegeben, der angibt, ob die angegebene Relation wahr oder falsch ist.

Operator Funktion Beispiel
== gleich
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x and y are not equal, hence false.
bool result = x == y;
!= ungleich
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x and y are not equal, hence true.
bool result = x != y;
> größer
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x is not greater than y, hence false.<br />
bool result = x > y;
< kleiner
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x is smaller than y, hence true.
bool result = x < y;
>= größer oder gleich
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x is not greater than or equal to y, hence false.
bool result = x >= y;
<= kleiner oder gleich
C#
1
2
3
4
5
int x = 2;
int y = 3;

// x is smaller than or equal to y, hence true.
bool result = x <= y;

Es gilt als guter Stil, die beiden Operanden mitsamt dem relationalen Operator zu klammern, um die Lesbarkeit zu verbessern. An Stelle von
C#
1
bool result = foo == bar;
würde man also
C#
1
bool result = (foo == bar);
schreiben.

Relationale Operatoren können prinzipiell zwar auf alle Wertetypen angewandt werden, allerdings ist dies nur begrenzt sinnvoll. Da Dezimalzahlen von Prozessoren intern nicht exakt dargestellt werden können, kann man sich nicht darauf verlassen, dass zwei anscheinend gleich große Zahlen des Typs float, double oder decimal bei einem Vergleich mit dem Operator == das Literal true als Ergebnis liefern. Dezimalzahlen sollten immer nur mit Hilfe von >, <, >= und <= verglichen werden.

Verweistypen können zumindest mit Hilfe der Operatoren == und != verglichen werden, wobei dies eine andere Semantik als bei Wertetypen hat. Während bei Wertetypen der tatsächliche Wert verglichen wird, wird bei Verweistypen lediglich die Referenz verglichen. Sofern zwei Variablen also eine Referenz auf das identische Objekt enthalten, wird bei einem Vergleich mit == das Literal true zurückgeliefert. Enthalten sie aber Referenzen auf zwei verschiedene Objekte, die zwar in ihren Werten, aber nicht in ihrer Objektidentität übereinstimmen, so liefert der Vergleich das Literal false.
C#
1
2
3
4
5
6
7
8
9
10
11
12
ComplexNumber foo = new ComplexNumber(23, 42);
ComplexNumber bar = foo;

// Returns true, since foo and bar reference the identical
// object.
Console.WriteLine(foo == bar);

y = new ComplexNumber(23, 42);

// Returns false, since foo and bar reference different
// objects, even if they have the same value.
Console.WriteLine(foo == bar);

Logische Operatoren

Während relationale Operatoren einen Vergleich zwischen den beiden Operanden durchführen, verknüpfen logische Operatoren diese. Logische Operatoren können im Gegensatz zu den anderen Operatoren nur auf Operanden des Typs bool angewandt werden und liefern auch als Ergebnis einen Wert des Typs bool.

Operator Funktion Beschreibung Beispiel
&& und Ergibt true, wenn beide Operanden true sind.
C#
1
2
3
4
5
bool x = true;
bool y = false;

// x and y are not both true, hence false.
bool result = x && y;
|| oder Ergibt true, wenn mindestens einer der beiden Operanden true ist.
C#
1
2
3
4
5
bool x = true;
bool y = false;

// At least one of x and y is true, hence true.
bool result = x || y;
^ exklusives oder Ergibt true, wenn genau einer der beiden Operanden true ist.
C#
1
2
3
4
5
bool x = true;
bool y = false;

// x is true, y is not, hence true.
bool result = x ^ y;
! nicht Ergibt true, wenn der Operand false ist, und umgekehrt.
C#
1
2
3
4
bool x = true;

// x is true, hence false.
bool result = !x;

C# verwendet bei der Auswertung logischer Operatoren die sogenannte Kurzschlussevaluierung. Dies bedeutet, dass für die Auswertung eines Operators unter Umständen nicht alle Operanden überprüft werden - ist beispielsweise bei einer und-Verknüpfung bereits der erste Operand false, so kann das Ergebnis nicht true sein, unabhängig davon, welchen Wert der zweite Operand aufweist. Daher wird dieser nicht mehr überprüft und direkt false zurückgegeben.

Bitweise Operatoren

Bitweise Operatoren ähneln logischen Operatoren sehr stark, allerdings werden sie nicht für Wahrheitswerte, sondern für Ganzzahlen verwendet. Die Überprüfung findet dementsprechend auch nicht auf den Wahrheitswerten der Operanden statt, sondern auf Bitebene der Operanden.

Operator Funktion Beschreibung Beispiel
& und Ergibt 1, wenn beide Bits 1 sind.
C#
1
2
3
4
5
int x = 23;
int y = 42;

// x is binary 010111 and y is binary 101010, hence 000010, which is 2.
int result = x & y;
| oder Ergibt 1, wenn mindestens eines der beiden Bits 1 ist.
C#
1
2
3
4
5
int x = 23;
int y = 42;

// x is binary 010111 and y is binary 101010, hence 111111, which is 63.
int result = x | y;
^ exklusives oder Ergibt 1, wenn genau eines der beiden Bits 1 ist.
C#
1
2
3
4
5
int x = 23;
int y = 42;

// x is binary 010111 and y is binary 101010, hence 111101, which is 61.
int result = x ^ y;
~ nicht Ergibt 1, wenn das Bit 0 ist, und umgekehrt.
C#
1
2
3
4
int x = 23;

// x is binary 010111, hence 101000, which is -24 due to internal reasons.
int result = ~x;
<< verschieben nach links Schiebt alle Bits um die angegebene Anzahl nach links.
C#
1
2
3
4
int x = 23;

// x is binary 010111, hence 101110, which is 46.
int result = x << 1;
>> verschieben nach rechts Schiebt alle Bits um die angegebene Anzahl nach rechts.
C#
1
2
3
4
int x = 23;

// x is binary 010111, hence 001011, which is 11.
int result = x >> 1;

Bitweise Operatoren werden häufig verwendet, um zu überprüfen, ob einzelne Bits gesetzt sind, oder um diese zu setzen beziehungsweise zu löschen. Ebenso wie arithmetische Operatoren können bitweise Operatoren mit dem Zuweisungsoperator zu einer verkürzten Schreibweise zusammengezogen werden.

Zeichenkettenoperatoren

Zeichenketten erfahren in C# eine Sonderbehandlung. Obwohl sie technisch gesehen Verweistypen sind, verhalten sie sich größtenteils wie Wertetypen, was ihre Handhabung teilweise deutlich erleichtert.

So liefert der Vergleich von zwei Strings mit Hilfe von == und != ein Ergebnis, als wären Strings Wertetypen - enthalten sie den gleichen Text, sind sie gleich. Außerdem können Strings mit Hilfe von <, >, <= und >= alphabetisch miteinander verglichen werden. Ein String gilt dann als kleiner als ein anderer, wenn er im Alphabet vorher einzuordnen ist.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
string foo = "Hello";
string bar = "World";

// foo and bar do not contain the same text, hence false.
bool result = (foo == bar);

// foo and bar do not contain the same text, hence true.
result = (foo != bar);

// foo is alphabetically prior to bar, hence true.
result = (foo < bar);

// foo is alphabetically not superior to bar, hence false.
result = (foo > bar);

// foo is alphabetically prior or equal to bar, hence true.
result = (foo <= bar);

// foo is alphabetically neither superior nor equal to bar,
// hence false.
result = (foo >= bar);
Zudem können Strings mit dem Operator + aneinander gehängt werden, so dass sie einen neuen zusammenhängenden String ergeben. Dieser Vorgang wird auch als Konkatenation bezeichnet.
C#
1
2
3
4
5
string foo = "Hello ";
string bar = "world!";

// The result is "Hello world!".
string result = foo + bar;
Ob ein String leer ist, kann geprüft werden, indem er mit dem leeren String "" verglichen wird. Alternativ kann auch die Eigenschaft Empty der Klasse String verwendet werden. Eine weitere Möglichkeit ist, die Eigenschaft Length des Strings zu prüfen, ob diese dem Wert Null entspricht.

Da der Vergleich auf die Länge auf Grund der internen Organisation von Strings am schnellsten ausgeführt werden kann, gilt es als guter Stil, diese Variante zu verwenden.
C#
1
2
3
4
5
6
string foo = "Hello";

// foo is not empty, hence false.
bool result = (foo == "");
result = (foo == String.Empty);
result = (foo.Length == 0);
Ob ein String leer oder eventuell sogar null ist, kann mit der statischen Methode IsNullOrEmpty der Klasse string ermittelt werden.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
string foo = null;

// foo is null, hence true.
bool result = String.IsNullOrEmpty(foo);

foo = "";

// foo is empty, hence true.
result = String.IsNullOrEmpty(foo);

foo = "Hello";

// foo is neither null nor empty, hence false.
result = String.IsNullOrEmpty(foo);

Operatorreihenfolge

Falls mehrere Operatoren gleichzeitig in einer Anweisung verwendet werden, werden diese zunächst von links nach rechts verarbeitet. Allerdings verfügen einige Operatoren über eine höhere Priorität als andere, so dass die Verarbeitung diesen Regeln folgt - ähnlich den Regeln bei den arithmetischen Operatoren.

Die Operatoren haben folgende Priorität, wobei die höchstpriorisierten Operatoren an oberster Stelle stehen:

Operatoren
(), []
++ (postfix), -- (postfix), ++ (präfix), -- (präfix), ~, !
*, /, %
+, -
>>, >>>, <<
>, >=;, <, <=
==, !=
&
^
|
&&
||
?:
=, <operator>=

Mit Hilfe von Operatoren können nun die meisten Methoden der Klasse ComplexNumber implementiert werden.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Executes when storing begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when storing has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public sealed class ComplexNumber : IPersistable
    {
        #region Properties
        #endregion

        #region Events
        #endregion

        #region Methods
        // ...

        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // Calculate the conjugation.
            this._imaginaryPart *= -1;
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is used
        /// as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // Add the summand to the current complex
            this._realPart += summand.RealPart;
            this._imaginaryPart += summand.ImaginaryPart;
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The real number that is used as
        /// summand.</param>
        public void Add(float summand)
        {
            // Add the summand to the current complex
            this._realPart += summand;
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // Multiply the factor with the current complex
            // number.
            float? newRealPart =
                (this.RealPart * factor.RealPart) -
                (this.ImaginaryPart * factor.ImaginaryPart);
            float? newImaginaryPart =
                (this.RealPart * factor.ImaginaryPart) +
                (this.ImaginaryPart * factor.RealPart);

            // Assign the new values to the current complex
            // number.
            this.RealPart = newRealPart;
            this.ImaginaryPart = newImaginaryPart;
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The real number that is
        /// used as factor.</param>
        public void Multiply(float factor)
        {
            // Multiply the factor with the current complex
            // number.
            this.Multiply(new ComplexNumber(factor));
        }
        #endregion

        #region Constructors
        #endregion
    }
}

Überladen von Operatoren

Zwar ist es mit Hilfe dieser Operatoren nun möglich, Berechnungen mit komplexen Zahlen durchzuführen, allerdings muss für jede einzelne Operation eine eigene Methode aufgerufen werden. So muss beispielsweise für die Addition zweier komplexer Zahlen die entsprechende Methode Add verwendet werden, welche die als Parameter übergebene komplexe Zahl zu der addiert, an der die Methode aufgerufen wird.
C#
1
firstComplexNumber.Add(secondComplexNumber);
Obwohl dieses Vorgehen funktioniert, entspricht die sich dadurch ergebende Syntax nicht der aus der Mathematik gewohnten Schreibweise, in der zwischen den beiden zu addierenden Zahlen ein + angegeben wird.

Der Grund, warum eine Addition komplexer Zahlen mit Hilfe des Symbols + in C# nicht funktioniert, ist offensichtlich: Die Klasse ComplexNumber ist für .NET eine beliebige, vom Benutzer definierte Klasse, deren mathematische Eigenheiten nur dem Entwickler bekannt sind. In C# ist also schlichtweg nicht definiert, welche Bedeutung dem Symbol + für komplexe Zahlen innewohnt.

Allerdings lassen sich Operatoren - und nichts anderes stellt das Symbol + in C# dar - für benutzerdefinierte Klassen überladen, so dass eigene Datentypen in mathematischen Ausdrücken unter Verwendung der klassischen Syntax miteinander verrechnet werden können.

Um einen Operator zu überladen, genügt es, eine entsprechende Methode innerhalb der Klasse zu definieren, für die der Operator gelten soll. Als Methodenname wird dabei der Operator an sich angegeben, zusätzlich muss ihm allerdings noch das Schlüsselwort operator vorangestellt werden. Außerdem muss beachtet werden, dass operatorüberladende Methoden immer klassengebunden, also mit dem Schlüsselwort static gekennzeichnet werden müssen.

Als Parameter werden dabei die einzelnen Operanden angegeben, die miteinander verrechnet werden sollen. Die Anzahl der Parameter bestimmt sich dabei aus der Anzahl der Operanden, die für den jeweiligen Operator benötigt werden. Die Operatoren + und * erwarten beispielsweise zwei Operanden, der Operator ! hingegen nur einen.

Der Typ des Rückgabewerts entspricht in jedem Fall der Klasse, in welcher der überladene Operator definiert wird.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Executes when storing begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when storing has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public sealed class ComplexNumber : IPersistable
    {
        #region Properties
        #endregion

        #region Events
        #endregion

        #region Operators
        /// <summary>
        /// Adds the specified complex numbers.
        /// </summary>
        /// <param name="firstSummand">The complex number
        /// that is used as first summand.</param>
        /// <param name="secondSummand">The complex number
        /// that is used as second summand.</param>
        /// <returns>The sum of the specified complex
        /// numbers.</returns>
        public static ComplexNumber operator +(
            ComplexNumber firstSummand,
            ComplexNumber secondSummand)
        {
            // Add the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    firstSummand.RealPart,
                    firstSummand.ImaginaryPart);
            result.Add(secondSummand);

            // Return the result to the caller.
            return result;
        }

        /// <summary>
        /// Multiplies the specified complex numbers.
        /// </summary>
        /// <param name="firstFactor">The complex number
        /// that is used as first factor.</param>
        /// <param name="secondFactor">The complex number
        /// that is used as second factor.</param>
        /// <returns>The product of the specified complex
        /// numbers.</returns>
        public static ComplexNumber operator *(
            ComplexNumber firstFactor,
            ComplexNumber secondFactor)
        {
            // Multiply the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    firstFactor.RealPart,
                    firstFactor.ImaginaryPart);
            result.Multiply(secondFactor);

            // Return the result to the caller.
            return result;
        }
        #endregion

        #region Methods
        #endregion

        #region Constructors
        #endregion
    }
}
Überladene Operatoren ermöglichen in C# nicht nur, gleichartige Operanden miteinander zu verrechnen, sondern es können auch Operatoren verschiedener Typen angegeben werden. Allerdings muss mindestens einer der Operanden immer der Klasse entsprechen, in welcher der Operator überladen wird. Es ist also beispielsweise nicht möglich, in der Klasse ComplexNumber die Addition für zwei Operanden des Typs int zu überladen.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Executes when storing begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when storing has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void StoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring begins.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoringEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Executes when restoring has finished.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The event arguments.</param>
    public delegate void RestoredEventHandler(
        object sender, EventArgs eventArguments);

    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public sealed class ComplexNumber : IPersistable
    {
        #region Properties
        #endregion

        #region Events
        #endregion

        #region Operators
        /// <summary>
        /// Adds the specified complex numbers.
        /// </summary>
        /// <param name="firstSummand">The complex number
        /// that is used as first summand.</param>
        /// <param name="secondSummand">The complex number
        /// that is used as second summand.</param>
        /// <returns>The sum of the specified complex
        /// numbers.</returns>
        public static ComplexNumber operator +(
            ComplexNumber firstSummand,
            ComplexNumber secondSummand)
        {
            // Add the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    firstSummand.RealPart,
                    firstSummand.ImaginaryPart);
            result.Add(secondSummand);

            // Return the result to the caller.
            return result;
        }

        /// <summary>
        /// Adds the specified summand to the specified
        /// complex number.
        /// </summary>
        /// <param name="complexNumber">The complex number
        /// that is used as first summand.</param>
        /// <param name="summand">The summand that is used
        /// as second summand.</param>
        /// <returns>The sum of the specified complex number
        /// and the specified summand.</returns>
        public static ComplexNumber operator +(
            ComplexNumber complexNumber, float summand)
        {
            // Add the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    complexNumber.RealPart,
                    complexNumber.ImaginaryPart);
            result.Add(summand);

            // Return the result to the caller.
            return result;
        }

        /// <summary>
        /// Multiplies the specified complex numbers.
        /// </summary>
        /// <param name="firstFactor">The complex number
        /// that is used as first factor.</param>
        /// <param name="secondFactor">The complex number
        /// that is used as second factor.</param>
        /// <returns>The product of the specified complex
        /// numbers.</returns>
        public static ComplexNumber operator *(
            ComplexNumber firstFactor,
            ComplexNumber secondFactor)
        {
            // Multiply the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    firstFactor.RealPart,
                    firstFactor.ImaginaryPart);
            result.Multiply(secondFactor);

            // Return the result to the caller.
            return result;
        }

        /// <summary>
        /// Multiplies the specified complex number with
        /// the specified factor.
        /// </summary>
        /// <param name="complexNumber">The complex number
        /// that is used as first factor.</param>
        /// <param name="factor">The factor that is used
        /// as second factor.</param>
        /// <returns>The product of the specified complex
        /// number and the specified factor.</returns>
        public static ComplexNumber operator *(
            ComplexNumber complexNumber, float factor)
        {
            // Multiply the two complex numbers.
            ComplexNumber result =
                new ComplexNumber(
                    complexNumber.RealPart,
                    complexNumber.ImaginaryPart);
            result.Multiply(factor);

            // Return the result to the caller.
            return result;
        }
        #endregion

        #region Methods
        #endregion

        #region Constructors
        #endregion
    }
}
Bei der Überladung von Operatoren gibt es drei Einschränkungen, die beachtet werden müssen: Zum einen können in C# einige Operatoren nicht überladen werden, dazu zählen insbesondere der Zuweisungsoperator, sämtliche Klammern und auch alle Operatoren, die nicht durch ein Symbol wie + oder *, sondern durch ein Schlüsselwort repräsentiert werden.

Zum zweiten können einige Operatoren nur paarweise überladen werden, was insbesondere für die relationalen Operatoren gilt. Das heißt, wird beispielsweise der Operator > überladen, so muss auch der entsprechende Operator < überladen werden.

Zu guter letzt ist es nicht möglich, die verkürzte Schreibweise, die einen Operator mit dem Zuweisungsoperator verbindet, getrennt von dem eigentlichen Operator zu überladen. Wird also zum Beispiel der Operator + überladen, so wird dadurch implizit auch der Operator += überladen.