Neben Verweis- und Wertetypen verfügt C# seit der Version 2.0 über eine weitere Art von
Typen, die nullbaren Wertetypen. Diese entsprechen einem Hybriden zwischen Verweis- und
Wertetypen, da sie in ihrer Funktion den Wertetypen entsprechen, zusätzlich allerdings
den Wert
null annehmen können, der üblicherweise Verweistypen vorbehalten ist.
Mit nullbaren Wertetypen ist es beispielsweise möglich, den Wert eines Wertetyps als
unbekannt zu kennzeichnen. Ohne die Möglichkeit,
null zuordnen zu können, müsste dafür
ein konkreter Wert verwendet werden, wie beispielsweise die Zahl Null oder eine leere
Zeichenkette. Allerdings entfiele in diesem Fall die Möglichkeit, zwischen dem
tatsächlichen Wert Null beziehungsweise der leeren Zeichenkette und einem unbekannten
Wert zu unterscheiden.
Intern werden nullbare Wertetypen durch einen Verweistyp dargestellt, indem dieser als
Container für den Wertetyp dient und zusätzliche Eigenschaften bereitstellt, um mit dem
Wert
null umgehen zu können.
Definiert wird ein nullbarer Wertetyp, indem an die Typdefinition ein ? angehängt wird.
Um die Klasse ComplexNumber derart zu erweitern, dass der Real- und der Imaginärteil einer
komplexen Zahl der Wert
null angegeben werden kann, muss in den entsprechenden Definitionen
der Typ
float? an Stelle von
float verwendet werden.
| 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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
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
// ...
/// <summary>
/// Gets or sets the real part.
/// </summary>
/// <value>The real part.</value>
public float? RealPart
{
get
{
return this._realPart;
}
set
{
this._realPart = value;
}
}
/// <summary>
/// Gets or sets the imaginary part.
/// </summary>
/// <value>The imaginary part.</value>
public float? ImginaryPart
{
get
{
return this._imaginaryPart;
}
set
{
this._imaginaryPart = value;
}
}
#endregion
#region Events
#endregion
#region Methods
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the ComplexNumber
/// type using default values.
/// </summary>
public ComplexNumber()
: this(null, null)
{
}
/// <summary>
/// Initializes a new instance of the ComplexNumber
/// type using the specified real value.
/// </summary>
/// <param name="realPart">The real part.</param>
public ComplexNumber(float? realPart)
: this(realPart, null)
{
}
/// <summary>
/// Initializes a new instance of the ComplexNumber
/// type using the specified real and imaginary
/// values.
/// </summary>
/// <param name="realPart">The real part.</param>
/// <param name="imaginaryPart">The imaginary
/// part.</param>
public ComplexNumber(
float? realPart, float? imaginaryPart)
{
// Set default values for the real and
// imaginary part.
this.RealPart = realPart;
this.ImaginaryPart = imaginaryPart;
}
#endregion
}
}
|
Da die Typen
float und
float? für C# verschieden sind, müssen nicht nur die Definitionen
der Felder, sondern auch die der zugehörigen Eigenschaften, Methoden und Konstruktoren
angepasst werden.
Insbesondere in den Konstruktoren muss entschieden werden, mit welchen Standardwerten die
Felder initialisiert werden sollen - bislang war es die Zahl Null, in der neuen Version
werden die Felder statt dessen mit
null initialisiert, falls kein konkreter Wert angegeben
wird. Dies entspricht der Bedeutung des Literals
null, dass der eigentliche
Wert nämlich nicht bekannt ist.