Ausdrücke
Konvertieren
Bei der Division und Modulodivision von Typen wurde erwähnt, dass das Ergebnis einer
Verknüpfung von zwei Operanden mit Hilfe eines arithmetischen Operators immer dem
Typ der beiden Operanden entspricht, weshalb es insbesondere bei der Division von
ganzzahligen Datentypen zu Problemen kommen kann, da der Dezimalteil verloren
geht.
Die vermeintliche Lösung, das Ergebnis einer solchen Division einer Variablen vom Typ
float oder
double zuzuweisen, erweist
sich bei näherer Betrachtung als unzureichend, da die Dezimalstellen des Ergebnisses
bereits im Speicher abgeschnitten werden, noch bevor die Zuweisung an die aufnehmende
Variable ausgeführt wird.
Eine Möglichkeit, diesem Problem zu begegnen, liegt darin, mindestens einen der
Operanden in einen Typ zu wandeln, der über Dezimalstellen verfügt. Da es aber nicht
in jedem Fall möglich ist, einen Operanden von vornherein als entsprechenden Typ zu
deklarieren, muss dies gegebenenfalls während der Ausführung zur Laufzeit geschehen.
Dieser Vorgang wird als konvertieren oder casten bezeichnet.
Die einfachste Möglichkeit, einen Typ in einen anderen zu konvertieren, ist, den
eigentlichen Wert dem neuen Typen zuzuweisen. Da hierbei die Umwandlung in den neuen
Typ implizit geschieht, wird diese Art der Konvertierung als implizite Konvertierung
bezeichnet.
| C# |
1
2
3
4
5
6
|
// Assign a value to an int variable.
int x = 23;
// Assign the value to a long variable. The value is
// implicitly casted from int to long.
long y = x;
|
Sofern der Wertebereich des Typs, in den konvertiert wird, umfangreicher ist als der
des Typs, der den ursprünglichen Wert enthält, funktioniert dieses Verfahren ohne
weiteres. Auf diese Art können beispielsweise
int in
long und
float in
double konvertiert werden. Beim Versuch, eine Konvertierung
in umgekehrter Richtung durchzuführen, meldet C# allerdings einen Fehler, da potenziell
ein Werteverlust eintreten könnte.
Soll eine solche Konvertierung dennoch ausgeführt werden, muss dies mit Hilfe einer
expliziten Konvertierung geschehen. Bei dieser wird dem umzuwandelnden Wert der
Typ, in den konvertiert wird, innerhalb runder Klammern vorangestellt.
| C# |
1
2
3
4
5
6
|
// Assign a value to a long variable.
long x = 23;
// Assign the value to an int variable. The value needs to
// be casted explicitly.
int y = (int)x;
|
Die explizite Konvertierung ermöglicht auch die Division zweier Ganzzahlen unter
Beibehaltung des Dezimalteils des Ergebnisses. Dazu muss lediglich einer der beiden
Operanden in einen Dezimaltyp konvertiert werden.
| C# |
1
2
3
4
5
6
7
8
9
|
int x = 23;
int y = 42;
// The quotient is 0, since no cast has been done.
float quotient = x / y;
// The quotient is 0.547619, since one of the operands has
// been casted explicitly to a decimal type.
quotient = (float)x / y;
|
Boxing
Im Rahmen der Vererbung wurde erwähnt, dass alle Typen von
object ableiten. Aus diesem Grund ist es möglich,
jeden beliebigen Typ nach
object zu konvertieren, sogar
dann, wenn es sich bei dem ursprünglichen Typ um einen Werte- und nicht um einen
Verweistyp handelt.
Während sich bei einem Verweistyp lediglich der Typ des Verweises ändert, ändert
sich bei einem Wertetyp zusätzlich die Art, wie der Wert gespeichert wird, da die
Anwendung bei einem Verweistyp nur mit einem Verweis auf die eigentlichen Daten,
bei einem Wertetyp aber direkt mit den eigentlichen Daten arbeitet. Daher ist es
notwendig, einen Wertetyp, der nach
object konvertiert
werden soll, zunächst in einen zusätzlichen Verweistyp zu verpacken, auf den dann
wiederum ein Verweis vom Typ
object angelegt werden
kann.
Dieses Verpacken wird als Boxing bezeichnet und von C# intern automatisch
durchgeführt, sobald ein Wertetyp in einen Verweistyp konvertiert wird. Obwohl man
sich also nicht händisch um das Boxing kümmern muss, sollte man sich während der
Entwicklung dieses Vorgangs im Hintergrund immer bewusst sein, da dieser nicht nur
zusätzlichen Speicher verbraucht, sondern auch Zeit benötigt. Insofern sollte
Boxing nur mit Bedacht und gezielt an einigen Stellen eingesetzt werden.
Nachdem ein Wertetyp in einen Verweistyp verpackt wurde, kann dieser Vorgang
auch wieder umgekehrt werden, um aus dem Verweistyp den ursprünglichen Wertetyp
zu erhalten. Dies wird als Unboxing bezeichnet und folgt den gleichen Regeln wie
das Boxing.
| C# |
1
2
3
4
5
6
7
|
int valueType = 23;
// Box the value type and create a reference type.
object referenceType = valueType;
// Unbox the reference type.
valueType = (int)referenceType;
|
Benutzerdefiniertes Konvertieren
Prinzipiell können mit diesen Möglichkeiten zum einen beliebige Typen in
object konvertiert werden, zum anderen können Typen
ineinander konvertiert werden, die über eine gemeinsame Basis verfügen oder
die in einer Vererbungshierarchie zueinander stehen. Gelegentlich kann es jedoch
nützlich sein, eine eigene Konvertierung definieren zu können, um beispielsweise
eine komplexe Zahl mit Hilfe ihres Absolutbetrags in float? zu konvertieren.
Diese Konvertierung kann so wohl implizit wie auch explizit implementiert werden.
In beiden Fällen muss die bestehende Klasse durch eine weitere Operatorüberladung
ergänzt werden, wobei der Zieltyp der Konvertierung als Operatorname dient. Außerdem
muss eines der beiden Schlüsselwörter
implicit und
explicit angegeben werden, um zu definieren, ob die
Konvertierung in den Zieltyp implizit ausgeführt werden kann, oder ob zwingend eine
explizite Konvertierung benötigt wird.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Executes when storing begins.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event arguments.</param>
public delegate void StoringEventHandler(
object sender, EventArgs eventArguments);
/// <summary>
/// Executes when storing has finished.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event arguments.</param>
public delegate void StoredEventHandler(
object sender, EventArgs eventArguments);
/// <summary>
/// Executes when restoring begins.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event arguments.</param>
public delegate void RestoringEventHandler(
object sender, EventArgs eventArguments);
/// <summary>
/// Executes when restoring has finished.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The event arguments.</param>
public delegate void RestoredEventHandler(
object sender, EventArgs eventArguments);
/// <summary>
/// Represents a complex number.
/// </summary>
public sealed class ComplexNumber : IPersistable
{
#region Properties
#endregion
#region Events
#endregion
#region Operators
// ...
/// <summary>
/// Casts the specified complex number to float?.
/// </summary>
/// <param name="complexNumber">The complex number
/// that shall be casted.</param>
/// <returns>A float? representation of the specified
/// complex number.</returns>
public static implicit operator float?(
ComplexNumber complexNumber)
{
// Return the complex number as float? by using
// its absolute value.
return complexNumber.AbsoluteValue;
}
#endregion
#region Methods
#endregion
#region Constructors
#endregion
}
}
|
Ein Rückgabetyp muss im Gegensatz zu den bisherigen Operatorüberladungen nicht
angegeben werden, da sich dieser aus dem Operator an sich bereits ergibt. Zu
beachten ist bei der Definition benutzerdefinierter Konvertierungsoperatoren noch,
dass es nicht für einen Zieltyp zugleich so wohl einen impliziten wie auch einen
expliziten Operator geben kann. Die Definition mehrerer Konvertierungsoperatoren
ist nur möglich, sofern sich diese durch ihren Zieltyp unterscheiden.
Konvertierbarkeit
Obwohl C# zahlreiche Möglichkeiten bietet, zwischen verschiedenen Typen zu
konvertieren, kann es dennoch vorkommen, dass ein bestimmter Typ schlichtweg nicht
in einen anderen Typ konvertierbar ist. Wird ein solcher Versuch trotzdem
unternommen, tritt ein Fehler auf und die Ausführung der Anwendung wird
abgebrochen.
Um dies zu verhindern, enthält C# drei Schlüsselwörter, mit denen geprüft werden
kann, ob sich ein Typ in einen bestimmten Zieltyp konvertiert lässt. Das einfachste
dieser Schlüsselwörter ist
typeof, das ein Objekt der
Klasse Type zurückgibt, das Informationen zu dem jeweiligen Typ enthält. Eine
Analyse dieses Typobjekts ermöglicht dann im weiteren Verlauf, zu bestimmen, ob
und auf welche Art konvertiert werden kann.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Get type information on the Program type.
Type type = typeof(Program);
// Print the type's full name to the console.
Console.WriteLine(type.FullName);
}
}
}
|
Häufig ist der Einsatz eines kompletten Typobjekts allerdings zu aufwändig, da
nur von Interesse ist, ob ein Typ überhaupt in einen bestimmten Zieltyp konvertiert
werden kann. Dazu dient das Schlüsselwort
is, das je
nach Konvertierbarkeit
true oder
false
an den Aufrufer zurückgibt.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a string and store it within an object
// reference.
object value = "Hello world!";
// Execute code depending on the type of the
// value.
if (value is string)
{
// Cast the value to a string.
string valueAsString = (string)value;
// TODO gr: Do something ...
// 2008-01-03
}
}
}
}
|
Schließlich gibt es noch das Schlüsselwort
as, das prinzipiell
ebenfalls eine explizite Konvertierung durchführt, im Fehlerfall aber nicht die
Ausführung der Anwendung abbricht, sondern das Literal
null
zurückgibt.
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
using System;
namespace GoloRoden.GuideToCSharp
{
/// <summary>
/// Represents the application class.
/// </summary>
public class Program
{
/// <summary>
/// Executes the application.
/// </summary>
public static void Main()
{
// Define a string and store it within an object
// reference.
object value = "Hello world!";
// Try to cast the value to string.
string valueAsString = value as string;
// If the cast was successful, execute some
// code.
if (valueAsString != null)
{
// TODO gr: Do something ...
// 2008-01-03
}
}
}
}
|