Datentypen
Werttypen und Referenztypen
Es gibt zwei grundlegende Kategorien von Datentypen: Werttypen (Value Types) und Referenztypen (Reference Types). Hier ist eine Erklärung beider Typen auf Deutsch:
-
Werttypen (Value Types):
- Werttypen repräsentieren Daten, die direkt im Speicher gespeichert werden.
- Diese Datentypen speichern den Wert selbst, nicht die Referenz auf ein Objekt.
- Werttypen werden auf dem Stack gespeichert, was bedeutet, dass sie schneller auf den Speicher zugegriffen werden können.
- Beispiele für Werttypen sind int, float, char, struct und enum.
-
Referenztypen (Reference Types):
- Referenztypen repräsentieren Daten, indem sie auf Objekte im Heap-Speicher verweisen.
- Sie speichern die Referenz auf ein Objekt, nicht den Wert selbst.
- Referenztypen werden auf dem Heap-Speicher allokiert, was bedeutet, dass sie mehr Speicherplatz benötigen und langsameren Zugriff auf den Wert ermöglichen.
- Beispiele für Referenztypen sind Klassen (class), Zeichenketten (string), Arrays und benutzerdefinierte Objekte.
Hier ist ein einfaches Beispiel in C#:
// Werttypen
int zahl1 = 10;
int zahl2 = zahl1; // Hier wird eine Kopie von zahl1 erstellt.
// Referenztypen
string text1 = "Hallo";
string text2 = text1; // Hier wird keine Kopie des Textes erstellt; beide variablen verweisen auf dasselbe Objekt im Speicher.
Es ist wichtig zu beachten, dass bei Werttypen Kopien der Werte erstellt werden, während bei Referenztypen Kopien der Referenzen erstellt werden, was dazu führen kann, dass Änderungen an einer Variable auch andere Variablen beeinflussen, die auf dasselbe Objekt verweisen.
// Werttypen-Beispiel
int zahl1 = 10;
int zahl2 = zahl1; // Hier wird eine Kopie von zahl1 erstellt.
Console.WriteLine("Werttypen:");
Console.WriteLine($"zahl1: {zahl1}, zahl2: {zahl2}");
zahl1 = 20; // Änderung von zahl1 hat keinen Einfluss auf zahl2.
Console.WriteLine($"zahl1 nach Änderung: {zahl1}, zahl2: {zahl2}");
Console.WriteLine();
// Referenztypen-Beispiel
List<int> liste1 = new List<int>() { 1, 2, 3 };
List<int> liste2 = liste1; // Hier wird keine Kopie der Liste erstellt; liste2 verweist auf dieselbe Liste.
Console.WriteLine("Referenztypen:");
Console.Write("liste1: ");
foreach (int item in liste1)
{
Console.Write($"{item} ");
}
Console.WriteLine();
Console.Write("liste2: ");
foreach (int item in liste2)
{
Console.Write($"{item} ");
}
Console.WriteLine();
liste1.Add(4); // Änderung an liste1 wirkt sich auch auf liste2 aus.
Console.Write("liste1 nach Änderung: ");
foreach (int item in liste1)
{
Console.Write($"{item} ");
}
Console.WriteLine();
Console.Write("liste2 nach Änderung: ");
foreach (int item in liste2)
{
Console.Write($"{item} ");
}
Console.WriteLine();
Besonderheit von string als Referenztypen
In C# ist string
ein besonderer Fall, der oft als Referenztyp behandelt wird, aber tatsächlich eine Ausnahme darstellt. Ein string
wird normalerweise als Referenztyp wahrgenommen, da es sich um eine Klasse (System.String
) handelt und auf dem Heap-Speicher gespeichert wird. Das bedeutet, dass eine Variable vom Typ string
tatsächlich eine Referenz auf ein string
-Objekt darstellt.
string text1 = "Hallo";
string text2 = text1; // Hier wird keine Kopie des Textes erstellt; beide Variablen verweisen auf dasselbe string-Objekt im Speicher.
In diesem Fall zeigen text1
und text2
auf dasselbe string
-Objekt "Hallo" im Speicher.
Allerdings gibt es eine wichtige Besonderheit bei string
in C#: Es ist unveränderlich (immutable). Das bedeutet, wenn Sie den Inhalt eines string
ändern, wird tatsächlich ein neues string
-Objekt im Speicher erstellt, und die Variable zeigt dann auf das neue Objekt. Beispielsweise:
string text1 = "Hallo";
string text2 = text1; // Hier wird keine Kopie des Textes erstellt.
text1 = "Hallo, Welt!"; // Ein neues string-Objekt wird erstellt, das "Hallo, Welt!" enthält, und text1 zeigt darauf.
Console.WriteLine(text1); // Ausgabe: "Hallo, Welt!"
Console.WriteLine(text2); // Ausgabe: "Hallo"
Obwohl string
also wie ein Referenztyp aussieht, verhält es sich aufgrund seiner Unveränderlichkeit anders als andere Referenztypen. Wenn Sie den Inhalt eines string
ändern, wird tatsächlich ein neues Objekt erstellt, anstatt den vorhandenen Wert zu ändern.
Datentypenübersicht
.NET-Laufzeittyp | C#-Alias | CLS-konform | Wertebereich |
---|---|---|---|
Byte | byte | ja | 0 ... 255 |
SByte | sbyte | nein | –128 ... 127 |
Int16 | short | ja | –215 … 215 – 1 |
UInt16 | ushort | nein | 0 … 65.535 |
Int32 | int | ja | –231 … 231 – 1 |
UInt32 | uint | nein | 0 ... 232 – 1 |
Int64 | long | ja | –263 … 263 – 1 |
UInt64 | ulong | nein | 0 … 264 – 1 |
Single | float | ja | 1,4 × 10–45 bis 3,4 × 1038 |
Double | double | ja | 5,0 × 10–324 bis 1,7 × 10308 |
Decimal | decimal | ja | +/–79E27 ohne Dezimalpunktangabe; +/–7.9E–29, falls 28 Stellen hinter dem Dezimalpunkt angegeben werden. Die kleinste darstellbare Zahl beträgt +/–1.0E–29. |
Char | char | ja | Unicode-Zeichen zwischen 0 und 65.535 |
String | string | ja | ca. 231 Unicode-Zeichen |
Boolean | bool | ja | true oder false |
Object | object | ja | Eine Variable vom Typ Object kann jeden anderen Datentyp enthalten, ist also universell. |
Wertebereiche ausgeben
Console.WriteLine($"double from: {double.MinValue}");
Console.WriteLine($"double from: {double.MaxValue}");
Dezimalzahlen
Alle Zahlen müssen bevor sie in eine Variable geschrieben werden, erst in den Speicher in ein Literal geschrieben werden. Dieser ist für Dezimalzahlen double und für Ganzzahlen int.
Daher kommt es bei der ersten Anweisung zu einem Fehler.
// Fehlerhaft
float value = 0.123456789;
// Korrekt
float value2 = 0.123456789f;
Vererbungsstruktur von Zahlen
Typsuffix
Suffix | Fließkommatyp |
---|---|
F oder f | float |
D oder d | double |
M oder m | decimal |
Wertebereiche ausgeben
Console.WriteLine($"double from: {double.MinValue}");
Console.WriteLine($"double from: {double.MaxValue}");
Digit Seperator
long value = 3_584_354_135_131;
Zeichenketten
char letter = 'D';
string text = "Hello world!";
Implizite Datentyp-Konvertierung
Implizite Datentyp-Konvertierung erfolgt, wenn der zuweisende Typ kleiner ist.
Für bool, string und object gibt es keine implizite Konvertierung.
Beispiel
long value1 = 12000;
// Fehlerhaft
int value2 = value1;
// Korrekt
double value3 = value1;
Explizite Konvertierung
Es können nicht alle Konvertierung mit dem Konvertierungsoperator umgewandelt werden. An manchen Stellen kann dafür die Convert Klasse verwendet werden.
short number1 = 430;
// Fehlerhaft, liefert 174 da die Zahl nach 8 byte abgeschnitten wird
byte number2 = (byte)number1;
Console.WriteLine(number2);
int value4 = 0;
// Fehlerhaft
//bool bolVal = (bool)value4;
// Korrekt
bool bolVal2 = Convert.ToBoolean(value2);
Es empfiehlt sich immer die Convert-Klasse zum explizite konvertieren zu verwenden, da es sonst zu versteckten Fehlern kommen kann die schwer zu erkennen sind.
Implizit typisierte Variablen
Mit dem Schlüsselwort var kann Datentyp implizit bestimmt werden. Das bedeutet, dass er automatisch vom Compiler zugewiesen wird.
var valueInteger = 8;
Console.WriteLine($"Dei Variable valueInteger ist vom Typ {valueInteger.GetType()}");
var valueDouble = 8d;
Console.WriteLine($"Dei Variable valueInteger ist vom Typ {valueDouble.GetType()}");
var list = new List<int>();
Console.WriteLine($"Dei Variable list ist vom Typ {list.GetType()}");
Nullable Datentypen
Nullable-Werttypen (auch als "Nullable Types" oder "Nullable Value Types" bezeichnet) sind eine spezielle Art von Werttypen, die die Möglichkeit bieten, auch den Wert null
(keinen Wert) zu repräsentieren. Dies ist nützlich, wenn Sie Werttypen verwenden möchten, aber auch die Option benötigen, einen Wert als "nicht vorhanden" darzustellen.
Das Schlüsselwort, um einen Werttyp als nullable zu deklarieren, ist Nullable<T>
, wobei T
der zugrunde liegende Werttyp ist, den Sie nullable machen möchten. Es gibt auch eine syntaktische Abkürzung für die Verwendung von nullable Werttypen, indem ein ?
nach dem zugrunde liegenden Werttyp hinzugefügt wird.
Hier ist ein Beispiel für die Verwendung von nullable Werttypen:
int? nullableZahl = null; // Hier ist nullableZahl null, da int? einen nullable int darstellt.
if (nullableZahl.HasValue)
{
Console.WriteLine($"Wert von nullableZahl: {nullableZahl.Value}");
}
else
{
Console.WriteLine("nullableZahl hat keinen Wert (null).");
}
Datentypen umwandeln mit as
und is
-
is
-Operator (Typüberprüfung):Der
is
-Operator wird verwendet, um zu überprüfen, ob ein Objekt eine Instanz eines bestimmten Typs oder einer bestimmten Klasse ist. Wenn die Überprüfung erfolgreich ist, gibtis
true
zurück, andernfallsfalse
.Beispiel:
object obj = "Hallo, Welt!";
if (obj is string)
{
// Objekt ist eine Zeichenkette
string text = (string)obj; // Typumwandlung ist erforderlich
Console.WriteLine(text);
}
else
{
Console.WriteLine("Objekt ist kein String");
} -
as
-Operator (Typumwandlung):Der
as
-Operator wird verwendet, um ein Objekt in einen anderen Typ zu konvertieren, sofern die Konvertierung möglich ist. Wenn die Konvertierung erfolgreich ist, gibtas
eine Instanz des Zieltyps zurück, andernfallsnull
.Beispiel:
object obj = "Hallo, Welt!";
string text = obj as string; // Konvertiert zu string oder gibt null zurück
if (text != null)
{
// Die Konvertierung war erfolgreich
Console.WriteLine(text);
}
else
{
Console.WriteLine("Konvertierung zu string fehlgeschlagen");
}
Wichtige Punkte:
-
Der
is
-Operator überprüft den Typ und gibt entwedertrue
oderfalse
zurück, ohne das Objekt selbst zu ändern. -
Der
as
-Operator versucht, das Objekt in den angegebenen Typ zu konvertieren. Wenn die Konvertierung fehlschlägt, wirdnull
zurückgegeben. -
Verwenden Sie den
as
-Operator, wenn Sie eine sichere Typumwandlung durchführen möchten, ohne eine Ausnahme auszulösen. Es ist nützlich, wenn Sie nicht sicher sind, ob die Konvertierung erfolgreich sein wird. -
Verwenden Sie den
is
-Operator, wenn Sie lediglich den Typ überprüfen und anschließend eine explizite Typumwandlung (Casting) durchführen möchten, wenn die Überprüfung erfolgreich ist.
Diese beiden Operatoren sind hilfreiche Werkzeuge bei der Arbeit mit objektorientiertem Code, wenn Sie die Typen von Objekten überprüfen oder sie in andere Typen umwandeln müssen, ohne Fehler zu verursachen.