Nach fast 14 Jahren des initialen Beitrags hat sich in C# doch einiges geändert.
Boxing und Unboxing
Boxing ist die Art und Weise wie mit Typen in C# umgegangen wird.
Die Konvertierung von einem konkreten Value-Type (zB
int) in den Typ
object nennt sich
Boxing.
int i = 123;
// boxing von i auf o
object o = i;
Das Gegenteil von Boxing ist das Unboxing; hier erfolgt die Konvertierung von object auf den jeweiligen Value-Type.
object o = 123;
int i = (int)o; // unboxing
Achtung: Beim Unboxing muss beachtet werden, dass der ursprüngliche Value-Type verwendet wird, ansonsten quittiert das die Runtime mit einer
InvalidCastException.
// Funktioniert:
int i1 = 123;
object o = i1;
int i2 = (int)o;
// InvalidCastException:
int i1 = 123;
object o = i1;
uint i2 = (uint)o; // der Ursprung ist int und nicht uint!
Mehr Informationen in der Dokumentation von
Boxing und Unboxing.
Unsicheres Casten aka hartes Konvertieren
Das unsichere Konvertieren wirft eine InvalidCastException, wenn der Ziel-Type nicht passt.
object o = "Hallo";
string s = (string)o; // funktioniert
int wert = (int)o; // InvalidCastException
Sicheres Casten mit dem as-Operator
Aufgrund der potentiellen InvalidCastException gab es seit Beginn von C# auch den as-Operator, mit dem ein sicheres Konvertieren möglich ist - allerdings nur für Referenztypen.
Sofern der Type nicht identisch ist wird keine Exception geworfen, sondern
null gesetzt.
object s = "Hallo";
MyClass myClassInstance = s as MyClass;
if (myClassInstance == null)
{
// Konvertierung fehlgeschlagen
}
else
{
// Konvertierung war erfolgreich
}
Mehr Informationen in der Dokumentation von
as-Operator.
Sicheres Casten mit dem is-Operator
Der as-Operator funktioniert nur mit Referenztypen; daher nicht mit int und Co. Die Alternative an dieser Stelle ist der is-Operator.
Mit dem is-Operator kann vor dem Konvertieren geprüft werden, ob der Typ passt:
int wert;
if (s is int)
{
wert = (int)s;
}
else
{
// Es kann nicht konvertiert werden, weil s kein int ist.
}
Der is-Operator kann mit Wert- und Referenztypen verwendet werden.
Mehr Informationen in der Dokumentation von
is-Operator.
Pattern Matching mit is - Type Pattern
Mit C# 7 wurde das Pattern Matching eingeführt. Pattern Matching fällt unter die Kategorie Syntaxzucker und verkürzt die Schreibweise.
Im Falle des Pattern Matching mit dem is-Operator kann dabei die Prüfung und das Casting in einen Befehl verkürzt werden.
// Ohne Pattern Matching:
Person p1 = new Person();
object o = p1;
Person p2;
if (o is Person)
{
p2 = (Person)o;
// p2 ist nun eine Instanz von Person
}
// Mit Pattern Matching:
Person p1 = new Person();
object o = p1;
if (o is Person p2)
{
// p2 ist nun eine Instanz von Person
}
Das Pattern Matching an dieser Stelle funktioniert auch mit var:
Person p1 = new Person();
object o = p1;
if (o is var v)
{
Console.WriteLine($"var ist vom Typ: {v.GetType().Name}"); // Ausgabe: var ist vom Typ: Person
}
Achtung: aus Compiler-Sicht ist
var vom Typ
object; damit stehen die Eigenschaften nur zur Laufzeit zur Verfügung!
Mehr Informationen in der Dokumentation von
Type Testing mit Pattern Matching.
Operatoren
Die Umwandlung von
int in
uint ist streng genommen keine Konvertierung, sondern eine Umwandlung; im Englischen als
reassigment bezeichnet.
Dass im Sprachgebrauch von C# trotzdem damit umgegangen werden kann als sei es eine Konvertierung, können Operatoren verwendet werden.
Ein solcher explziter Operator ist in diesem Fall für die Umwandlung von int in uint verantwortlich:
public static explicit operator int(uint value) => Convert.ToInt32(value);
int i = 1;
uint u = (uint)i; // die harte Umwandlung mit (int) stellt die explizite Konvertierung dar
Neben der
expliziten Konvertierung, die das Voranstellen des Zieltyps erfordert, gibt es auch die
implizite Konvertierung.
Hierbei erkennt der Compiler automatisch den entsprechenden Operator, um die Konvertierung durchzuführen. Ist kein Operator deklariert wird ein Compiler-Error generiert, der beklagt, dass nur eine explizite und keine implizite Konvertierung gefunden wurde.
public class MyCustomInteger
{
public int Value { get; }
public MyCustomInteger(int value)
{
Value = value;
}
public static explicit operator MyCustomInteger(int value) => new MyCustomInteger(value);
// Verwendung:
MyCustomInteger myValue = (MyCustomInteger)1;
// oder
public static implicit operator MyCustomInteger(int value) => new MyCustomInteger(value);
// Verwendung:
MyCustomInteger myValue = 1;
}
Achtung: es können nicht beide Operator-Arten mit den gleichen Parameter gleichzeitig deklariert werden.
Mehr Informationen in der Dokumentation von
Benutzerdefinierte Konvertierungsoperatoren.