Datentypen und Datenstrukturen
Werte- und Referenztypen kennen?
Wertetypen wie bool, int und struct liegen meistens auf dem Stack und enthalten direkt den tatsächlichen Wert.
Referenztypen string, class, int[] liegen meisten auf dem Heap und enthalten eine Referenz auf das Objekt.
- Warum wurden so viele Typen erstellt?
- Früher zur Speicheroptimierung
- Mittlerweile nicht mehr so oft verwendet, weil es nicht optimiert ist
Casting
Casting ist der Umwandlung von einem Datentyp in einen anderen.
Unterschieden wird zwischen implizit und explizit Casting.
Implizitem Casting wird verwendet wenn keine Informationen verloren gehen.
int i = 10;
double d = i; // implizit castingExplizitem Casting wird verwendet wenn Informationen verloren gehen.
double d = 10.5;
int i = (int)d; // explizit casting
// i wird nur zu 10.float und double?
float ist eine 32-Bit Fließkommazahl mit 7 Nachkommastellen.
double ist eine 64-Bit Fließkommazahl mit 15 Nachkommastellen.
Bei float können schneller Rundungsfehler auftreten, aber auch double kann ungenau sein, da beide binär sind und Dezimalzahlen nicht exakt darstellen können.
double d = 0.1 + 0.2; // 0.30000000000000004
Um genaue Dezimalzahlen zu speichern, kann man decimal verwenden.
decimal d = 0.1m + 0.2m; // 0.3 - m steht für decimalWas sind enums?
Enums sind Ganzzahlwerte. Man definiert eine feste Liste von Werten. Sie sind standardmäßig vom Typ int und starten bei 0.
enum Color {
Red,
Green,
Blue
}
Color color = Color.Red;
if(color == Color.Red) {
Console.WriteLine("Red");
}Enums können auch einen festen Wert definiert haben:
enum Color {
Red = 10,
Green = 20,
Blue = 30
}Aufbau eines Strings
Ein String ist ein Referenztyp, der aus einer Länge und char Zeichen besteht.
Sie sind Immutable, d.h. sie können nicht verändert werden.
Strings besitzen keinen Setter und können nur ausgelesen werden mit char c = string[index];
Änderungen erzeugen immer einen neuen String string newString = string.Replace("old", "new");
Warum?
Strings sind immutable, da sie performanter sind und sicherer. Strings können sensible Daten enthalten und sind thread-safe.
Möchte man Strings oft verändern, nutzt man StringBuilder statt string.
StringBuilder stringBuilder = new StringBuilder("Hello, World!");
stringBuilder.Append("Hello, World!");
stringBuilder.Insert(0, "Hello, ");
stringBuilder.Remove(0, 7);
stringBuilder.Replace("Hello,", "Hi, ");
stringBuilder.ToString();String Zeichen auslesen können und über String loopen
Auslesen:
char c = string[index];Durchlaufen:
foreach(char c in string) {
Console.WriteLine(c);
}3 Wege der String Konkatination kennen
Operator:
string s = "Hello" + "World";Methode:
string s = string.Concat("Hello", "World");
string s = string.Join(" ", "Hello", "World");StringBuilder:
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuilder.Append("World");
stringBuilder.Insert(0, "Hello, ");
stringBuilder.Remove(0, 7);
stringBuilder.Replace("Hello,", "Hi, ");
stringBuilder.ToString();
string result = stringBuilder.ToString();Arrays kennen
Arrays sind feste Listen von Elementen mit fester Länge und Indexzugriff ab 0.
Erstellen:
int[] numbers = new int[10];
int[] numbers = { 1, 2, 3, 4, 5 };Zugriff:
int number = numbers[0]; // Lesen
numbers[0] = 10; // SchreibenDurchlaufen:
for(int i = 0; i < numbers.Length; i++) {
Console.WriteLine(numbers[i]);
}
foreach(int number in numbers) {
Console.WriteLine(number);
}Besondere Methoden bei Arrays:
Copy()- Kopiert die Elemente eines Arrays in ein anderes ArrayClone()- Kopiert die Elemente eines Arrays in ein neues ArrayGetLength()- Gibt die Länge des Arrays zurückSort()- Sortiert die Elemente des ArraysReverse()- Kehrt die Reihenfolge der Elemente des Arrays umFind()- Findet das erste Element, das die angegebene Bedingung erfülltFindAll()- Findet alle Elemente, die die angegebene Bedingung erfüllenFindIndex()- Findet den Index des ersten Elements, das die angegebene Bedingung erfülltFindLastIndex()- Findet den Index des letzten Elements, das die angegebene Bedingung erfüllt
Was ist ein Indexer?
Indexer ermöglichen den Zugriff auf ein Objekt wie auf eine Array (person[0]).
public Person this[int index] {
get { return _persons[index]; }
set { _persons[index] = value; }
}Auflistungsklasse (List<T>)
Sequenzen:
List<T>- Dynamische Liste von ElementenT[](Array) - Statische Liste von ElementenLinkedList<T>- Dynamische Liste von Elementen mit Zeiger auf den Vorgänger und Nachfolger
Stapel und Warteschlange:
Stack<T>- Stapel von Elementen (Last In First Out)Queue<T>- Warteschlange von Elementen (First In First Out)
Menge:
HashSet<T>- Menge von Elementen ohne Duplikate
Key-Value Paare:
Dictionary<TKey, TValue>- Dictionary von Elementen mit Key und Value. Werte über Key auslesbar.SortedDictionary<TKey, TValue>- Dictionary von Elementen mit Key und Value. Werte über Key auslesbar, aber nicht über Value. Die Elemente sind sortiert.SortedList<TKey, TValue>- Dictionary von Elementen mit Key und Value. Werte über Key auslesbar, aber nicht über Value. Die Elemente sind sortiert.SortedSet<T>- Menge von Elementen ohne Duplikate. Die Elemente sind sortiert.
Array vs List
Arrays haben eine feste Größe nach der Initialisierung. Weitere Elemente können nicht hinzugefügt oder entfernt werden.
int[] numbers = new int[10];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;List<T> ist eine dynamische Liste von Elementen. Die Größe wird zur Laufzeit dynamisch angepasst und kann auch Elemente Hinzufügen oder Entfernen.
List<int> numbers = new List<int>();
numbers.Add(1);
numbers.Add(2);
numbers.Remove(1);Dictionarys
Dictionarys sind Key-Value Paare. Pro Schlüssel wird genau ein Element gespeichert. Fragt man einen Key ab, dann bekommt man schnell das Value zurück. Intern nutzt man eine Hashtable. Der Key wird gehashed und lässt sich leichter im Speicher finden.
var dict = new Dictionary<string, int>();
dict.Add("Sarah", 654422); // hinzufügen
dict["Erwin"] = 873587; // hinzufügen/überschreiben per Indexer
string number = dict["Sarah"]; // auslesen
bool hasSarah = dict.ContainsKey("Sarah");
if (dict.TryGetValue("Sarah", out int tel))
{
Console.WriteLine(tel);
}Der Unterschied zu Lookup Collections ist, dass ein Key nur einmal vorkommen kann.
Enumeratoren kennen
Enumeratoren legen fest, wie eine Sequenz von Elementen vorwärts durchlaufen wird. Sie werden bei foreach Schleifen verwendet.
IEnumerable<T> ist die Basisklasse für alle Enumeratoren und liefert einen Enumerator GetEnumerator() zurück.
Mit IEnumerator<T> kann man den Enumerator explizit erstellen und verwenden.
Bewegen tut sich der Enumerator mit MoveNext(), Current liefert das aktuelle Element und Reset() setzt den Enumerator auf den Anfang zurück.
yield return erzeugt automatisch einen Enumerator.
public class Students : IEnumerable {
private readonly List<Student> _students = [];
public IEnumerator<Student> GetEnumerator() {
return _students.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}Woraus besteht ein IEnumerator?
Current- Gibt das aktuelle Element an der Cursor Position zurückMoveNext()- Bewegt den Cursor um ein Element vorwärts (true wenn ein Element vorhanden ist, false wenn das Ende der Sequenz erreicht ist)Reset()- Setzt den Cursor auf den Anfang der Sequenz zurück
Unterschied zwischen IEnumerable und IQueryable?
- IEnumerable ist ein Interface, dass auf Arrays, Listen, HashSets etc. im Arbeitsspeicher angewendet werden kann.
- IQueryable ist eine Erweiterung von IEnumerable und für externe Datenquellen, wie Datenbanken gedacht. Die Logik wird dabei in eine andere Sprache übersetzt und auf dem Datenbankserver ausgeführt.
Der Nachteil an IEnumerable ist, dass alle Daten erst in den Speicher geladen werden müssen, was bei großen Datenmengen zu Performance-Problemen führen kann.
JSON
Grundprinzip des JSON Formats
JSON stellen Informationen strukturiert in einer Textform dar. Es ist sprachunabhängig und leicht lesbar.
- Strukturen: Es gibt nur zwei Grundstrukturen
Object- Key-Value Paare{"key": "value"}Array- Liste von Werten oder Objekten["value1", "value2", "value3"]
- Datentypen: String, Number, Boolean, null, Object, Array
Wann verwendet man JSON?
- Datenaustausch zwischen Client und Server
- Speicherung von Konfigurationsdaten
- Persistierung von Objektzuständen
Wie nutzt man JSON in .NET?
Benötigt weitere Informationen!
Man unterscheidet zwischen dem komfortablen (High-Level) und dem performanten (Low-Level) Weg.
High-Level (DOM / Objekt-Mapping)
Hierbei werden C# Objekte direkt in JSON umgewandelt und umgekehrt.
- Klasse:
JsonSerializer - Methoden:
.Serialize()und.Deserialize()
var json = JsonSerializer.Serialize(object); // Objekt in JSON umwandeln
var object = JsonSerializer.Deserialize<T>(json); // JSON in Objekt umwandelnLow-Level (Reader / Writer)
Hier wird mit dem Byte-Stream gearbeitet. Es ist performant, aber aufwendiger zu implementieren.
Schreiben: (Utf8JsonWriter)
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
writer.WriteStartObject(); // {
writer.WriteString("name", "John"); // "name": "John"
writer.WriteNumber("age", 20); // "age": 20
writer.WriteStartArray("Termine"); // "Termine": [
writer.WriteStringValue("2026-01-01"); // "2026-01-01"
writer.WriteEndArray(); // ]
writer.WriteEndObject(); // }
writer.Flush();Lesen: (Utf8JsonReader)
var reader = new Utf8JsonReader(json);
while (reader.Read()) {
switch(reader.TokenType) {
case JsonTokenType.StartObject:
break;
case JsonTokenType.PropertyName:
string propertyName = reader.GetString();
break;
case JsonTokenType.String:
string value = reader.GetString();
break;
}
}Warum ist manchmal manuelles Parsen notwendig?
Low-Level Parsing ist manchmal notwendig, wenn es komplexe Objektbeziehungen gibt die der Deserializer nicht automatisch auflösen kann. Außerdem ist es performanter, da es schneller ist und weniger Speicher benötigt, da nicht der gesamte JSON String im Speicher gelegt werden muss.