ein Projekt von goloroden.de
Skip Navigation Linksguide to C# > Guide > Nullbare Wertetypen

Nullbare Wertetypen

Was sind nullbare Wertetypen?

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.