# C#-Nachschlagewerk # Theorie ## Arrays in C# Arrays sind Sammlungen von Elementen eines bestimmten Typs, die über einen Index abgerufen werden können. [Übungsaufgabe](#Array-Basics) ### Allgemein **Arrays** sind grundlegende Datenstrukturen in C# und dienen zur Speicherung von Sammlungen von Elementen. Hier sind einige ausführliche Stichpunkte, die das Konzept von Arrays in C# und ihre Funktionsweise erklären: 1. **Definition**: Ein Array in C# ist eine Sammlung von Elementen, die denselben **Datentyp** haben und über einen **Index** zugänglich sind. 2. **Deklaration**: In C# wird ein Array mit der Syntax `Datentyp[] ArrayName` deklariert, z.B. `int[] myArray;`. 3. **Initialisierung**: Arrays in C# können bei der Erstellung initialisiert werden, indem man ihnen eine Liste von Werten zuweist oder die Größe angibt. Beispiele: `int[] myArray = new int[5];` oder `int[] myArray = {1, 2, 3, 4, 5};`. 4. **Index**: Jedes Element im Array hat einen eindeutigen Index, der seine Position innerhalb des Arrays angibt. Indices sind typischerweise ganze Zahlen, die bei 0 beginnen. 5. **Speicherung**: Arrays in C# speichern ihre Elemente in aufeinanderfolgenden **Speicheradressen**, was sie effizient für den Zugriff auf Elemente anhand ihres Indexes macht. Also um bei einem int-Array der Länge 5 `int[] test = new int[4]` auf den Index `3` zuzugreifen wird die Speicheradresse des ersten Elements `0` plus den Index mal der Länge des Datentyps (bei int `4 bytes`) gerechnet. Nehmen wir an Index 0 liegt an der "Speicheradresse" `8` dann liegt Index 3 bei `8+(3*4)` also `20`. 6. **Feste Größe**: Die Größe eines Arrays in C# ist festgelegt und kann nach der Erstellung nicht geändert werden. Um die Größe eines Arrays zu ändern, muss man normalerweise ein neues Array erstellen und die Elemente kopieren. 7. **Zugriff**: Elemente eines Arrays in C# können über ihren Index abgerufen oder geändert werden. 8. **Schleifen**: Arrays in C# können mit Schleifen durchlaufen werden, um auf ihre Elemente zuzugreifen oder sie zu bearbeiten. Häufig verwendete Schleifen sind `for`, `foreach` und `while`-Schleifen. 9. **Mehrdimensionale Arrays**: Arrays in C# können mehrere Dimensionen haben, wie z.B. 2D- oder 3D-Arrays. Mehrdimensionale Arrays sind im Grunde genommen Arrays von Arrays. Beispiele: `int[,] my2DArray = new int[3, 4];` oder `int[,,] my3DArray = new int[3, 4, 5];`. 10. **Jagged Arrays**: C# unterstützt auch unregelmäßige Arrays (jagged arrays), die Arrays von Arrays sind, wobei jedes innere Array eine unterschiedliche Länge haben kann. Beispiel: `int[][] myJaggedArray = new int[3][];`. 11. **Anwendungsbereiche**: Arrays in C# sind in vielen Bereichen der Programmierung und Informatik nützlich, z.B. bei der Speicherung von Daten, der Durchführung von Berechnungen und der Implementierung von Algorithmen. 12. **Alternative Datenstrukturen**: Arrays sind in C# nicht immer die beste Wahl für bestimmte Aufgaben. Andere Datenstrukturen wie `List<T>`, `HashSet<T>` oder `Dictionary<TKey, TValue>` können in bestimmten Situationen besser geeignet sein. ### Erstellen eines Arrays Ein Array wird erstellt, indem man den Typ der Elemente, gefolgt von eckigen Klammern `[]` und dann den Array-Namen angibt. Du musst auch die Größe des Arrays bei der Erstellung festlegen. Beispiel: ```csharp int[] zahlen = new int[5]; ``` #### Aufgabe: Erstellen eines String-Arrays Erstelle ein String-Array mit dem Namen `namen` und einer Größe von 3. <details> <summary>Lösung</summary> ```csharp string[] namen = new string[3]; ``` </details> ### Füllen eines Arrays Du kannst einem Array Werte zuweisen, indem du den Index verwendest. Beispiel: ```csharp zahlen[0] = 1; zahlen[1] = 2; zahlen[2] = 3; zahlen[3] = 4; zahlen[4] = 5; ``` #### Aufgabe: Füllen eines String-Arrays Fülle das `namen`-Array mit den Werten "Max", "Mia" und "Tom". <details> <summary>Lösung</summary> ```csharp namen[0] = "Max"; namen[1] = "Mia"; namen[2] = "Tom"; ``` </details> ### Zugreifen auf Elemente eines Arrays Um auf die Werte eines Arrays zuzugreifen, verwende den Index. Beispiel: ```csharp int zweiteZahl = zahlen[1]; ``` #### Aufgabe: Zugreifen auf ein Element eines String-Arrays Greife auf das zweite Element des `namen`-Arrays zu und speichere es in einer Variablen namens `zweiterName`. <details> <summary>Lösung</summary> ```csharp string zweiterName = namen[1]; ``` </details> ### Die `.Length`-Eigenschaft Die `.Length`-Eigenschaft gibt die Anzahl der Elemente in einem Array zurück. Beachte, dass der höchste Index immer um eins kleiner ist als die `.Length`, da die Indizierung bei 0 beginnt. Beispiel: ```csharp int[] zahlen = new int[6]; int arrayLength = zahlen.Length; // arrayLength ist 6 int hoechsterIndex = arrayLength - 1; // hoechsterIndex ist 5 ``` #### Frage: Was ist der höchste Index eines Arrays mit der Länge 10? <details> <summary>Antwort</summary> Der höchste Index eines Arrays mit der Länge 10 ist 9, da die Indizierung bei 0 beginnt. </details> ### Iterieren über ein Array Um über die Elemente eines Arrays zu iterieren, verwende eine `for`-Schleife oder eine `foreach`-Schleife. Beispiel: ```csharp for (int i = 0; i < zahlen.Length; i++) { Console.WriteLine(zahlen[i]); } foreach (int zahl in zahlen) { Console.WriteLine(zahl); } ``` #### Aufgabe: Iterieren über ein String-Array Iteriere über das `namen`-Array und gib jeden Namen aus. <details> <summary>Lösung</summary> ```csharp for (int i = 0; i < namen.Length; i++) { Console.WriteLine(namen[i]); } foreach (string name in namen) { Console.WriteLine(name); } ``` </details> ### Negativer Index In C# ist es nicht erlaubt, auf einen negativen Index zuzugreifen. Wenn du dies versuchst, wird eine `IndexOutOfRangeException` ausgelöst. #### Frage: Was passiert, wenn du auf einen negativen Index zugreifst, z.B. `array[-1]`? <details> <summary>Antwort</summary> Wenn du auf einen negativen Index zugreifst, wird eine `IndexOutOfRangeException` ausgelöst, da negative Indizes in C# nicht erlaubt sind. </details> ## Überladen von Operatoren in C\# In C# kannst du viele Operatoren überladen, d.h. du kannst ihre Funktionalität ändern, um sie für benutzerdefinierte Klassen oder Strukturen anzupassen. Die Syntax für das Überladen von Operatoren ist das Schlüsselwort `operator` gefolgt vom Operatorzeichen. ## Benutzerdefinierte Typen und Operatoren Du kannst einen vordefinierten C# Operator für einen benutzerdefinierten Typ überladen. Das bedeutet, du kannst eine eigene Implementierung einer Operation zur Verfügung stellen, wenn einer oder beide Operanden von diesem Typ sind. Um einen Operator zu deklarieren, verwendest du das Schlüsselwort `operator`. Eine Operatordeklaration muss die folgenden Regeln erfüllen: - Sie muss sowohl den `public` als auch den `static` Modifier enthalten. - Ein unärer Operator hat einen Eingabeparameter. Ein binärer Operator hat zwei Eingabeparameter. In jedem Fall muss mindestens ein Parameter den Typ T oder T? haben, wobei T der Typ ist, der die Operatordeklaration enthält. >[!question]- T >Das `T` ist eine Konvention und steht für "Type", du könntest aber auch jeden anderen Buchstaben oder Namen verwenden. Wenn du eine generische Klasse oder Methode schreibst, wie zum Beispiel `List<T>`, dann bedeutet das, dass die Liste Elemente eines beliebigen Typs `T` enthalten kann. >[!question]- T? >Das `T?` ist eine Erweiterung davon und wird in C# 8.0 und höher verwendet, um anzugeben, dass der Typ `T` nullable ist, d.h., dass er neben normalen Werten auch den Wert `null` annehmen kann. ### Überladbare Operatoren Die folgende Tabelle zeigt die Operatoren, die du überladen kannst: |Operatoren|Anmerkungen| |---|---| |+x, -x, !x, ~x, ++, --, true, false|Die Operatoren true und false müssen zusammen überladen werden.| |x + y, x - y, x * y, x / y, x % y, x & y, x \| y, x ^ y, x << y, x >> y|| |x == y, x != y, x < y, x > y, x <= y, x >= y|Müssen in Paaren überladen werden: == und !=, < und >, <= und >=.| ### Beispiel ```c# // Definiere eine Struktur namens 'Fraction' zur Repräsentation eines Bruchs. public readonly struct Fraction { // Definiere zwei private Eigenschaften 'num' und 'den' zur Speicherung von Zähler und Nenner. private readonly int num; private readonly int den; // Konstruktor der Struktur, der Zähler und Nenner annimmt. public Fraction(int numerator, int denominator) { // Wenn der Nenner null ist, wirf eine Ausnahme, da Division durch null nicht erlaubt ist. if (denominator == 0) { throw new ArgumentException("Nenner darf nicht null sein.", nameof(denominator)); } // Weise den Eingabewerten 'numerator' und 'denominator' den internen Eigenschaften 'num' und 'den' zu. num = numerator; den = denominator; } // Überlade den unairen + Operator. In diesem Fall wird einfach die Eingabe 'a' zurückgegeben. public static Fraction operator +(Fraction a) => a; // Überlade den unairen - Operator. In diesem Fall wird eine neue 'Fraction' mit dem negierten Zähler und dem unveränderten Nenner zurückgegeben. public static Fraction operator -(Fraction a) => new Fraction(-a.num, a.den); // Überlade den binären + Operator für die Addition zweier Brüche. public static Fraction operator +(Fraction a, Fraction b) => new Fraction(a.num * b.den + b.num * a.den, a.den * b.den); // Überlade den binären - Operator für die Subtraktion zweier Brüche. public static Fraction operator -(Fraction a, Fraction b) => a + (-b); // Nutze den zuvor definierten - und + Operator. // Überlade den * Operator für die Multiplikation zweier Brüche. public static Fraction operator *(Fraction a, Fraction b) => new Fraction(a.num * b.num, a.den * b.den); // Überlade den / Operator für die Division zweier Brüche. public static Fraction operator /(Fraction a, Fraction b) { // Kontrolliere, ob der Zähler des Bruchs 'b' null ist, um eine Division durch null zu verhindern. if (b.num == 0) { throw new DivideByZeroException(); } return new Fraction(a.num * b.den, a.den * b.num); } // Überschreibe die ToString-Methode, um den Bruch in Form von "Zähler / Nenner" darzustellen. public override string ToString() => $"{num} / {den}"; } ``` # Aufgaben ## Array-Basics **Pfad im Google Drive: `C#/Aufgaben/Array/Basics`** **Direktlink zum Drive Ordner:** [Link](https://drive.google.com/drive/folders/16DGIMG3r0Jz6_PBtJpYvcHCVQmT29TyS?usp=share_link) <details> <summary>Aufgabe</summary> ```csharp using System; class ArrayBasics { static void Main() { // Aufgabe 1: Deklariere ein leeres eindimensionales Array mit einer Größe von 3. // Hinweis: Verwende die Schreibweise "int[]" und "new int[3]". // Aufgabe 2: Fülle das Array aus Aufgabe 1 mit den Zahlen 1, 2 und 3. // Hinweis: Setze die Werte an den entsprechenden Positionen des Arrays. // Aufgabe 3: Deklariere und initialisiere ein neues eindimensionales Array mit den Zahlen 4, 5 und 6. // Hinweis: Verwende die Schreibweise "new int[] {...}". // Aufgabe 4: Gib das zweite Element des ersten Arrays aus. // Hinweis: Denke daran, dass Arrays bei 0 anfangen zu zählen. // Aufgabe 5: Ändere das dritte Element des zweiten Arrays auf 7. // Hinweis: Setze den Wert an der entsprechenden Position des Arrays. // Aufgabe 6: Gib alle Elemente des ersten Arrays in umgekehrter Reihenfolge aus. // Hinweis: Verwende eine for-Schleife und starte am Ende des Arrays. // Aufgabe 7: Schreibe eine Methode, die die Summe der Elemente eines eindimensionalen Arrays berechnet und zurückgibt. // Hinweis: Verwende eine for-Schleife und eine Variable zum Speichern der Summe. //Aufgabe 8: Schreibe eine Methode, die zwei eindimensionale Arrays als Eingabe erhält und ein neues Array zurückgibt, das die Elemente beider Arrays enthält. Die Reihenfolge der Elemente aus den beiden Eingabe-Arrays sollte im Ergebnis-Array beibehalten werden. } // Implementiere die Methode für Aufgabe 7 hier. } ``` </details> <details> <summary>Lösung</summary> ```csharp using System; class ArrayBasics { static void Main() { // Aufgabe 1: Deklariere ein leeres eindimensionales Array mit einer Größe von 3. int[] array1 = new int[3]; Console.Write("Aufgabe 1: Leeres Array: "); PrintArray(array1); Console.WriteLine(); // Aufgabe 2: Fülle das Array mit den Zahlen 1, 2 und 3. array1[0] = 1; array1[1] = 2; array1[2] = 3; Console.Write("Aufgabe 2: Array mit Zahlen 1, 2 und 3: "); PrintArray(array1); Console.WriteLine(); // Aufgabe 3: Deklariere und initialisiere ein eindimensionales Array mit den Zahlen 4, 5 und 6. int[] array2 = new int[] { 4, 5, 6 }; Console.Write("Aufgabe 3: Array mit Zahlen 4, 5 und 6: "); PrintArray(array2); Console.WriteLine(); // Aufgabe 4: Gib das zweite Element des ersten Arrays aus. Console.WriteLine("Aufgabe 4: Zweites Element des ersten Arrays: " + array1[1]); // Aufgabe 5: Ändere das dritte Element des zweiten Arrays auf 7. array2[2] = 7; Console.Write("Aufgabe 5: Zweites Array nach Änderung des dritten Elements: "); PrintArray(array2); Console.WriteLine(); // Aufgabe 6: Gib alle Elemente des ersten Arrays in umgekehrter Reihenfolge aus. Console.Write("Aufgabe 6: Elemente des ersten Arrays in umgekehrter Reihenfolge: "); for (int i = array1.Length - 1; i >= 0; i--) { Console.Write(array1[i] + " "); } Console.WriteLine(); // Aufgabe 7: Berechne die Summe der Elemente des zweiten Arrays. int sum = CalculateSum(array2); Console.WriteLine("Aufgabe 7: Summe der Elemente des zweiten Arrays: " + sum); } // Hilfsmethode zum Ausgeben eines Arrays. static void PrintArray(int[] array) { for (int i = 0; i < array.Length; i++) { Console.Write(array[i] + " "); } } // Methode zur Berechnung der Summe eines Arrays. static int CalculateSum(int[] array) { int sum = 0; for (int i = 0; i < array.Length; i++) { sum += array[i]; } return sum; } } ``` </details> ## Überladen von Operatoren mit komplexen Zahlen **Pfad im Google Drive: `C#/Aufgaben/Operatoren/`** **Direktlink zum Drive Ordner:** [Link](https://drive.google.com/open?id=1r5GBLUOur127jPggLjguLVP3igG2C4Jw&usp=drive_fs) <details> <summary>Aufgabe</summary> In dieser Übung erstellst Du eine Klasse `Komplex`, die eine komplexe Zahl repräsentiert. Eine komplexe Zahl besteht aus einem Realteil und einem Imaginärteil und wird üblicherweise in der Form $a + bi$ dargestellt, wobei $a$ der Realteil, $b$ der Imaginärteil und $i$ die imaginäre Einheit ist. ### Aufgaben 1. **Konstruktor**: Implementiere einen Konstruktor, um eine komplexe Zahl mit Real- und Imaginärteil zu erstellen. - _Tipp_: Du könntest zwei Parameter, `Realteil` und `Imaginärteil`, für den Konstruktor benötigen. 2. **ToString-Methode**: Implementiere eine `ToString` Methode, die eine komplexe Zahl in der Form $a + bi$ ausgibt. - _Tipp_: Nutze String-Formatierung, um die Ausgabe zu formatieren. 3. **Additionsoperator (`+`)**: Überlade den `+` Operator, um die Addition von zwei komplexen Zahlen zu ermöglichen. Die Formel zur Addition von komplexen Zahlen ist: - $(a + bi) + (c + di) = (a + c) + (b + d)i$ 4. **Subtraktionsoperator (`-`)**: Überlade den `-` Operator, um die Subtraktion von zwei komplexen Zahlen zu ermöglichen. Die Formel zur Subtraktion von komplexen Zahlen ist: - $(a + bi) - (c + di) = (a - c) + (b - d)i$ 5. **Multiplikationsoperator (`*`)**: Überlade den `*` Operator, um die Multiplikation von zwei komplexen Zahlen zu ermöglichen. Die Formel zur Multiplikation von komplexen Zahlen ist: - $(a + bi) * (c + di) = (ac - bd) + (ad + bc)i$ 6. **Divisionsoperator (`/`)**: Überlade den `/` Operator, um die Division von zwei komplexen Zahlen zu ermöglichen. Die Formel zur Division von komplexen Zahlen ist: - $(a + bi) / (c + di) = [(ac + bd) / (c^2 + d^2)] + [(bc - ad) / (c^2 + d^2)]i$ - _Tipp_: Achte darauf, dass Du nicht durch Null teilst! Viel Erfolg! </details> <details> <summary>Lösung</summary> ### Lösung ```c# public class Komplex { // Deklariere die Eigenschaften für den Real- und Imaginärteil. public double Realteil { get; } public double Imaginaerteil { get; } // Konstruktor, der den Real- und Imaginärteil setzt. public Komplex(double realteil, double imaginaerteil) { Realteil = realteil; Imaginaerteil = imaginaerteil; } // ToString-Methode, die eine komplexe Zahl in der Form "a + bi" ausgibt. public override string ToString() { return $"{Realteil} + {Imaginaerteil}i"; } // Überlade den + Operator für die Addition von zwei komplexen Zahlen. public static Komplex operator +(Komplex z1, Komplex z2) { return new Komplex(z1.Realteil + z2.Realteil, z1.Imaginaerteil + z2.Imaginaerteil); } // Überlade den - Operator für die Subtraktion von zwei komplexen Zahlen. public static Komplex operator -(Komplex z1, Komplex z2) { return new Komplex(z1.Realteil - z2.Realteil, z1.Imaginaerteil - z2.Imaginaerteil); } // Überlade den * Operator für die Multiplikation von zwei komplexen Zahlen. public static Komplex operator *(Komplex z1, Komplex z2) { return new Komplex(z1.Realteil * z2.Realteil - z1.Imaginaerteil * z2.Imaginaerteil, z1.Realteil * z2.Imaginaerteil + z1.Imaginaerteil * z2.Realteil); } // Überlade den / Operator für die Division von zwei komplexen Zahlen. public static Komplex operator /(Komplex z1, Komplex z2) { // Kontrolliere, ob der Divisor Null ist, um eine Division durch Null zu verhindern. if (z2.Realteil == 0 && z2.Imaginaerteil == 0) { throw new DivideByZeroException("Darf nicht durch Null teilen."); } double nenner = z2.Realteil * z2.Realteil + z2.Imaginaerteil * z2.Imaginaerteil; return new Komplex((z1.Realteil * z2.Realteil + z1.Imaginaerteil * z2.Imaginaerteil) / nenner, (z1.Imaginaerteil * z2.Realteil - z1.Realteil * z2.Imaginaerteil) / nenner); } } ``` </details>