Jeder kann coden / Programmieren & TicTacToe / C# Einführung
Structs in C#¶
In C# sind structs
(Strukturen) benutzerdefinierte Datentypen, die sich von Klassen dadurch unterscheiden, dass sie Werttypen statt Referenztypen sind. structs
werden im Stack und nicht im Heap gespeichert, was sie für leichte Datenstrukturen besonders effizient macht. Sie sind ideal für kleine Datenobjekte, die keine Vererbung oder Referenzsemantik benötigen. Typische Anwendungsfälle für structs
sind geometrische oder physikalische Werte, die sich häufig ändern und für die Speicher- und Leistungseffizienz wichtig ist.
Deklaration und Eigenschaften von structs¶
structs
können Methoden, Felder, Eigenschaften und Konstruktoren enthalten, jedoch keine Destruktoren und Vererbung (außer dem impliziten Erben vonSystem.ValueType
).structs
haben eine "Call-by-Value"-Semantik, was bedeutet, dass bei der Übergabe an Methoden eine Kopie des Wertes erstellt wird.
Beispiel: Struct zur Darstellung einer Messung der Luftqualität¶
Angenommen, wir möchten eine einfache Struktur zur Darstellung einer Luftqualitätsmessung in der Umweltinformatik erstellen. Hierzu erstellen wir ein struct
namens AirQualityMeasurement
, das folgende Eigenschaften enthält:
PM2_5
(Feinstaubbelastung in Mikrogramm pro Kubikmeter)PM10
(gröberer Feinstaubwert)Location
(Messort)
struct AirQualityMeasurement
{
public float PM2_5;
public float PM10;
public string Location;
// Konstruktor
public AirQualityMeasurement(float pm25, float pm10, string location)
{
PM2_5 = pm25;
PM10 = pm10;
Location = location;
}
// Methode zur Ausgabe der Messung
public void PrintMeasurement()
{
Console.WriteLine($"Messung am Standort {Location}:");
Console.WriteLine($"PM2.5: {PM2_5} µg/m³, PM10: {PM10} µg/m³");
}
}
// Initialisierung eines AirQualityMeasurement-Structs
AirQualityMeasurement measurement = new AirQualityMeasurement(25.3f, 45.7f, "Berlin-Mitte");
// Ausgabe der Messwerte
measurement.PrintMeasurement();
Messung am Standort Berlin-Mitte: PM2.5: 25,3 µg/m³, PM10: 45,7 µg/m³
In diesem Beispiel wird eine AirQualityMeasurement
-Instanz erstellt und die Messwerte für PM2.5 und PM10 an einem bestimmten Standort auf der Konsole ausgegeben.
Struct als Werttyp – Beispiel mit Parameterübergabe¶
Da structs
Werttypen sind, werden sie standardmäßig bei der Übergabe an eine Methode oder eine andere Struktur kopiert. Änderungen an der Kopie betreffen daher nicht das Original. Im folgenden Beispiel wird ein Struct für die Temperaturmessung (TemperatureMeasurement
) erstellt und an eine Methode übergeben, die den Wert modifiziert:
class TemperatureMeasurement
{
public float Temperature;
public string Location;
public TemperatureMeasurement(float temperature, string location)
{
Temperature = temperature;
Location = location;
}
public void PrintMeasurement()
{
Console.WriteLine($"Temperatur am Standort {Location}: {Temperature} °C");
}
}
static void ModifyTemperature(TemperatureMeasurement tempMeasurement)
{
// Ändern der Temperatur in der Kopie des structs
tempMeasurement.Temperature += 5.0f;
Console.WriteLine($"Geänderte Temperatur innerhalb der Methode: {tempMeasurement.Temperature} °C");
}
TemperatureMeasurement measurement = new TemperatureMeasurement(22.5f, "Hamburg");
// Vorherige Temperatur ausgeben
Console.WriteLine("Vor dem Methodenaufruf:");
measurement.PrintMeasurement();
// Methode aufrufen, die eine Kopie des Structs modifiziert
ModifyTemperature(measurement);
// Nach dem Methodenaufruf
Console.WriteLine("Nach dem Methodenaufruf:");
measurement.PrintMeasurement();
Vor dem Methodenaufruf: Temperatur am Standort Hamburg: 22,5 °C Geänderte Temperatur innerhalb der Methode: 27,5 °C Nach dem Methodenaufruf: Temperatur am Standort Hamburg: 27,5 °C
Erklärung¶
- Wir erstellen eine Instanz von
TemperatureMeasurement
mit einer Temperatur von 22.5 °C. - Die Methode
ModifyTemperature
ändert die Temperatur, aber da einstruct
als Werttyp übergeben wird, wird nur eine Kopie übergeben. - Daher bleiben die Änderungen lokal auf die Methode beschränkt, und die ursprüngliche Temperatur bleibt nach dem Methodenaufruf unverändert.
Fazit¶
Strukturen (structs
) sind eine wertvolle Möglichkeit, kleine, unveränderliche Datenobjekte in C# zu modellieren, die bei der Übertragung kopiert werden sollen, z. B. Messwerte in der Umweltinformatik.
Vertiefung¶
1. Unterschiede zwischen struct
und class
¶
Eigenschaft | struct |
class |
---|---|---|
Typ | Werttyp (stack-basiert) | Referenztyp (heap-basiert) |
Standardkonstruktor | Kein Parameterloser Standardkonstruktor | Immer ein parameterloser Standardkonstruktor vorhanden |
Vererbung | Keine Vererbung erlaubt | Unterstützt Vererbung |
Nullbarkeit | Kann mit Nullable<T> oder ? verwendet werden |
Kann direkt null sein |
Leistung | Schneller für kleine, kurzlebige Daten | Besser für komplexe Objekte |
2. Immutability (Unveränderlichkeit)¶
Während structs
per Standard veränderbar sind, ist es oft sinnvoll, sie unveränderlich zu machen, da dies Fehler bei der Bearbeitung vermeidet. Dies geschieht, indem Felder oder Eigenschaften als readonly
markiert werden.
Beispiel: Unveränderlicher Struct
struct ReadonlyMeasurement
{
public readonly float PM2_5 { get; }
public readonly float PM10 { get; }
public readonly string Location { get; }
public ReadonlyMeasurement(float pm25, float pm10, string location)
{
PM2_5 = pm25;
PM10 = pm10;
Location = location;
}
public void PrintMeasurement()
{
Console.WriteLine($"Messung: {Location}, PM2.5: {PM2_5}, PM10: {PM10}");
}
}
Vorteil: Der Zustand des struct
bleibt nach der Initialisierung unverändert, was Konsistenz und Thread-Sicherheit gewährleistet.
3. Speicherüberlegungen¶
Da structs
Werttypen sind, wird bei jeder Kopie eine neue Instanz erstellt. Dies kann bei großen structs
zu höheren Speicher- und Performancekosten führen. In solchen Fällen ist es besser, eine class
zu verwenden.
Richtlinie:
- Verwende
structs
, wenn sie klein (typischerweise weniger als 16 Byte) und kurzlebig sind. - Für komplexere oder größere Datenstrukturen sind Klassen besser geeignet.
4. Boxen und Unboxen¶
Wenn ein struct
in einen object
-Typ oder ein Interface-Typ konvertiert wird, findet ein Prozess namens Boxing statt, bei dem der Wert in eine Referenz auf dem Heap umgewandelt wird. Unboxing ist der umgekehrte Vorgang.
Beispiel: Boxing und Unboxing
struct AirQuality
{
public float PM2_5;
public AirQuality(float pm25)
{
PM2_5 = pm25;
}
}
AirQuality airQuality = new AirQuality(15.5f);
// Boxing
object boxed = airQuality;
// Unboxing
AirQuality unboxed = (AirQuality)boxed;
Console.WriteLine($"Ursprünglicher Wert: {airQuality.PM2_5}, Nach Unboxing: {unboxed.PM2_5}");
Ursprünglicher Wert: 15,5, Nach Unboxing: 15,5
Nachteil: Boxing/Unboxing ist ein teurer Vorgang und sollte vermieden werden, wenn hohe Performance wichtig ist.
5. Default-Werte für Structs¶
Ein struct
hat standardmäßig seine Felder mit Standardwerten (z. B. 0
für Zahlen, false
für boolesche Werte) initialisiert. Dies bedeutet, dass alle Felder eines struct
initialisiert sein müssen, bevor darauf zugegriffen wird.
Beispiel: Default-Werte
struct Measurement
{
public float Value;
public string Unit;
}
Measurement defaultMeasurement = new Measurement();
Console.WriteLine($"Default-Werte: Value={defaultMeasurement.Value}, Unit={defaultMeasurement.Unit}");
Default-Werte: Value=0, Unit=
6. Einsatzmöglichkeiten in der Umweltinformatik¶
Strukturen sind besonders nützlich in Bereichen wie Umweltinformatik, wo kleine, leichtgewichtige Datenobjekte häufig benötigt werden. Beispiele könnten sein:
- Geographische Koordinaten: Ein
struct
für Latitude/Longitude. - Umweltmessungen: Luftqualitäts- oder Wasserstandsmessungen.
- Sensorwerte: Temperatur, Luftdruck, Feuchtigkeit.
Beispiel: Sensor-Array mit structs
struct SensorData
{
public string SensorName;
public float Value;
public DateTime Timestamp;
public SensorData(string sensorName, float value, DateTime timestamp)
{
SensorName = sensorName;
Value = value;
Timestamp = timestamp;
}
public void PrintData()
{
Console.WriteLine($"Sensor: {SensorName}, Wert: {Value}, Zeit: {Timestamp}");
}
}
SensorData[] sensors = new SensorData[2]
{
new SensorData("Temperatur", 22.5f, DateTime.Now),
new SensorData("Luftfeuchtigkeit", 60.3f, DateTime.Now)
};
foreach (var sensor in sensors)
{
sensor.PrintData();
}
Sensor: Temperatur, Wert: 22,5, Zeit: 24.01.2025 13:34:04 Sensor: Luftfeuchtigkeit, Wert: 60,3, Zeit: 24.01.2025 13:34:04
Zusammenfassung¶
structs
sind leistungsstarke Werkzeuge für kleine, unveränderliche Datenobjekte, aber sie sollten nicht für große oder komplexe Strukturen verwendet werden.- Sie sind besonders in der Umweltinformatik nützlich, um Messdaten oder Sensordaten effizient zu modellieren.
- Achte auf mögliche Box-/Unbox-Operationen und nutze
readonly
für immutables Design.