Skip to Content
Semester 7Anwendungsentwicklung in C# (CSH)Datentypen und Datenstrukturen

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 casting

Explizitem 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 decimal

Was 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; // Schreiben

Durchlaufen:

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 Array
  • Clone() - Kopiert die Elemente eines Arrays in ein neues Array
  • GetLength() - Gibt die Länge des Arrays zurück
  • Sort() - Sortiert die Elemente des Arrays
  • Reverse() - Kehrt die Reihenfolge der Elemente des Arrays um
  • Find() - Findet das erste Element, das die angegebene Bedingung erfüllt
  • FindAll() - Findet alle Elemente, die die angegebene Bedingung erfüllen
  • FindIndex() - Findet den Index des ersten Elements, das die angegebene Bedingung erfüllt
  • FindLastIndex() - 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 Elementen
  • T[] (Array) - Statische Liste von Elementen
  • LinkedList<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ück
  • MoveNext() - 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
    1. Object - Key-Value Paare {"key": "value"}
    2. 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 umwandeln

Low-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.

Last updated on