4.5 Werttypen mit dem Wert Nothing 

Bei der Abfrage von Datenbanken können Sie leicht auf das Problem stoßen, dass Werte fehlen. Bei Referenztypen bereitet das nicht viel Kopfzerbrechen, da diese auch den Wert Nothing annehmen können. Anders sieht das bei Werttypen aus, da dann ja keine auf ein »Nichts« zeigende Referenz vorliegen kann, sondern Sie die Daten selbst »in der Hand« haben. Um diese Situation zu erfassen und dennoch die Typisierung eines Datums nicht zu verlieren, gibt es sogenannte »nullbare« Werttypen. (In anderen Programmiersprachen wird statt Nothing das Wort Null verwendet.) Sie erlauben alle Werte des Werttyps und den Wert Nothing. Technisch gesehen wird dabei ein Werttyp in einen Referenztyp verpackt. Da der Mechanismus des Ein- und Auspackens automatisch abläuft, besteht Ihre einzige Aufgabe darin, eine Variable eines solchen Typs mit einer der drei folgenden äquivalenten Syntaxvarianten zu deklarieren (kursiv gesetzte Teile wählen Sie nach Ihren Erfordernissen):
Modifikator Variable As Werttyp? |
Hinweis |
Arrays nullbarer Typen markieren die Variablen oder den Typ mit ? und (). |
Im folgenden Codefragment wird in der Methode Test() eine nullbare Fließkommazahl d deklariert und ohne vorherige Initialisierung ausgegeben. Danach werden ihr eine Zahl und Nothing zugewiesen und protokolliert. Der Rest des Moduls zeigt weitere Möglichkeiten nullbarer Datentypen. Dazu werden drei Werttypen definiert und in der Methode Nullbares() für Deklarationen verwendet. Bitte beachten Sie die Einschränkung des Typparameters Typ auf Strukturen. Nur mit dieser Einschränkung ist ein Typparameter in nullbaren Datentypen erlaubt.
'...\Datentypen\Nullable\Prinzip.vb |
Option Strict On
Namespace Datentypen
Module Prinzip
Sub Test()
Dim d As Double?
Console.WriteLine("Zahl {0}", d)
Console.WriteLine("Zahl hat Wert {0}", d.HasValue)
d = 3.1415
Console.WriteLine("Zahl {0}", d)
d = Nothing
Console.WriteLine("Zahl hat Wert {0}", d.HasValue)
Console.ReadLine()
End Sub
Structure Personenname : Friend Name As String : End Structure
Enum Grundfarbe : Rot : Grün : Blau : End Enum
Structure Zahl(Of T) : Friend nummer As T : End Structure
Sub Nullbares(Of Typ As Structure)()
Dim o As Typ?
Dim p As Personenname?
Dim f As Grundfarbe?
Dim i As Zahl(Of Short)?
End Sub
End Module
End Namespace
Wie Sie der Ausgabe entnehmen können, enthält die nicht initialisierte nullbare Fließkommazahl keinen Wert (bzw. Nothing, der sich als Leerstring "" nicht in der Ausgabe bemerkbar macht). Dabei ist die nullbare Variable an sich immer initialisiert, lediglich ihr Value-Feld hat ggf. den Wert Nothing.
Zahl
Zahl hat einen Wert: False
Zahl 3.1415
Zahl hat einen Wert: False
Bei der Zuweisung von Werten an nullbare Typen gibt es noch eine Stolperfalle. Der Wert kann gelesen, aber nicht explizit neu besetzt werden, da er schreibgeschützt ist. Daher erzeugt das folgende Codefragment einen Compilerfehler:
Dim zahl As Integer? = 45
zahl.Value = 3 'Compilerfehler: Value ist schreibgeschützt!!
Hinweis |
Die Methode GetValueOrDefault() der nullbaren Typen kann hilfreich sein, um einen Wert zu garantieren. |
4.5.1 Typumwandlungen 

Wenn ein Werttyp eine Konvertierung beherrscht, tut dies auch sein nullbares Pendant. Sie müssen also nicht das Rad neu erfinden und zum Beispiel die numerischen Umwandlungen erneut für die nullbaren Varianten definieren. Logischerweise kann der Automatismus aus einer expliziten Konvertierung nicht eine implizite machen und umgekehrt. Das folgende Codefragment definiert Variablen ganzer Zahlen verschiedenen Typs als Primitive und in den korrespondierenden nullbaren Varianten. Dann werden vier Zuweisungen nullbar = primitiv ausgeführt, die bei rein primitiven Typen erlaubt wären. Jede Zuweisung wird jeweils protokolliert.
'...\Datentypen\Nullable\Typumwandlung.vb |
Option Strict On
Namespace Datentypen
Module Typumwandlung
Sub Test()
Dim kurz As Short = 12, lang As Long = 17
Dim kurzNullbar As Short? = 2, langNullbar As Long? = 7
kurzNullbar = kurz
Console.WriteLine("kurzNullbar {0}", kurzNullbar)
langNullbar = kurz
Console.WriteLine("langNullbar {0}", langNullbar)
kurzNullbar = 453
kurz = CType(kurzNullbar, Short)
Console.WriteLine("kurz {0}", kurz)
kurzNullbar = Nothing
Try
kurz = CType(kurzNullbar, Short)
Catch ex As Exception
Console.WriteLine("Ausnahme {0}", ex.Message)
End Try
Console.ReadLine()
End Module
End Namespace
Die Ausgabe zeigt, dass die Zuweisungen Short?=Short, Long?=Short und Short= CType(Short?,Short) das gewünschte Ergebnis liefern, während die Umwandlung CType(Nothing,Short) zu einer Ausnahme führt.
kurzNullbar 12
langNullbar 12
kurz 453
Ausnahme Nullable object must have a value.
Wir können also Folgendes festhalten (W1 und W2 sind Werttypen, T1 ist nullbar):
- T1 = W2 ist erlaubt, wenn W1 = W2 erlaubt ist.
- T1 = Nothing : CType(T1, W2) führt zu einer Ausnahme (CType(Nothing, W2) ergibt »Null«, siehe Abschnitt 2.5.6, »Initialisierung von Variablen«).
Bezüglich Operatoren gibt es eine Ausnahme in Bezug auf den Übergang von einem nullbaren zu einem normalen Werttyp: Ist ein Operator für einen normalen Werttyp definiert, wird er auch für die nullbare Variante verwendet. Das folgende Beispiel definiert für eine Struktur Preis den unären +-Operator. In der Methode Test() wird dann in WriteLine mit dem Operator ein nullbarer Preis verwendet.
'...\Datentypen\Nullable\Operatoren.vb |
Option Strict On
Namespace Datentypen
Structure Preis
Public Euro As Double
Sub New(ByVal euro As Double)
Me.Euro = Euro
End Sub
Public Shared Operator +(ByVal z As Preis) As Double
Return 1.19 * z.Euro 'Mehrwertsteuer
End Operator
End Structure
Module Operatoren
Sub Test()
Dim wert As Preis? = New Preis(200)
Console.WriteLine("Wert {0}", +wert)
Console.ReadLine()
End Module
End Namespace
Die Ausgabe zeigt, dass eine Definition des Operators für Preis? nicht notwendig ist, da automatisch die Definition für Preis verwendet wird:
Wert 238
Bei Typkonvertierungsoperatoren wird speziell verfahren. Um eine Zuweisung T? = S? zu ermöglichen, wird Folgendes versucht:
- Operator für S?
R T?
- automatische Ergänzung der normalen Werttypumwandlungen um nullbare Varianten
(Aus S
R T wird S?
R T? über den Umweg S?
R S
R T
R S? bzw. S?
R T? für Nothing.)
Dabei sind zwei Regeln zu beachten:
- Benutzerdefinierte Operatoren haben Vorrang vor einer Automatik T
R T?.
- Maximal ein benutzerdefinierter Operator kommt zum Einsatz.
So komfortabel diese Umwandlungen auch sein mögen, sie ändern nichts daran, dass ein nullbarer Werttyp ein ganz anderer Typ ist als der zugrunde liegende Werttyp. Dies macht sich zum Beispiel in Zusammenhang mit von einem Werttyp implementierten Schnittstellen bemerkbar. Die nullbare Variante implementiert die Schnittstelle nicht und ist daher auch nicht vom Typ der Schnittstelle. Im folgenden Codefragment implementiert die Struktur Zahl die Schnittstelle Numerisch. Daher ist die vorletzte Zuweisung korrekt. Da die nullbare Variante Zahl? die Schnittstelle nicht implementiert, führt die letzte Zuweisung zu einem Compilerfehler.
'...\Datentypen\Nullable\Schnittstellen.vb |
Option Strict On
Namespace Datentypen
Module Schnittstellen
Interface Numerisch : End Interface
Structure Zahl : Implements Numerisch
Public Wert As Double
End Structure
Sub Test()
Dim wert As Zahl, wertNullbar As Zahl?
Dim ref As Numerisch = wert
Dim refNullbar As Numerisch = wertNullbar 'Compilerfehler!!
End Sub
End Module
End Namespace
Hinweis |
Diese Schnittstellenproblematik macht sich auch bei Beschränkungen von Typparametern bemerkbar. |
4.5.2 Logische Operatoren 

Da auch Wahrheitswerte zu den Werttypen gehören, können Sie diese auch in einer nullbaren Variante nutzen. Wie die üblichen Werte True und False mit dem neuen Wert Nothing kombiniert werden, testet das nächste Codefragment. In der Methode Test() werden die möglichen Werte mit zwei For Each-Schleifen durchlaufen und durch eine If-Bedingung nur die Kombinationen mit Nothing gewählt. Die logischen Verknüpfungen nutzen die Methode Report(), um eine Kurzschlussauswertung von AndAlso und OrElse zu erfassen. Findet sie statt, wird die Auswertung mit And bzw. Or wiederholt, um in der dann jeweils folgenden Ausgabe immer zwei Operanden zu haben.
'...\Datentypen\Nullable\Wahrheitswerte.vb |
Option Strict On
Namespace Datentypen
Module Wahrheitswerte
Dim aus As String
Function Report(ByVal b As Boolean?, _
Optional ByVal was As String = "") As Boolean?
If was = "" Then aus = ""
aus += If(was = "", "", was.PadRight(8)) & Bool(b)
Return b
End Function
Function Bool(ByVal b As Boolean?) As String
Return If(b.HasValue, b.ToString(), "Nothing").PadRight(8)
End Function
Sub Test()
For Each Kino As Boolean? In New Boolean?() {Nothing, True, False}
For Each Essen As Boolean? In New Boolean?() {Nothing, True, False}
If Kino.HasValue AndAlso Essen.HasValue Then Continue For
Dim wo As Boolean? = Report(Kino) AndAlso Report(Essen, "AndAlso")
If aus.Length < 24 Then wo = Report(Kino) And Report(Essen, "And")
Console.WriteLine(aus.PadRight(24) & ": " & Bool(wo))
wo = Report(Kino) OrElse Report(Essen, "OrElse")
If aus.Length < 24 Then wo = Report(Kino) Or Report(Essen, "Or")
Console.WriteLine(aus.PadRight(24) & ": " & Bool(wo))
Next Essen, Kino
Console.ReadLine()
End Module
End Namespace
Die Ausgabe zeigt, dass nur ein True in Or/OrElse und ein False in And/AndAlso einen normalen Wahrheitswert in Kombination mit Nothing ergibt. Alle anderen Verknüpfungen mit Nothing ergeben selbst wieder Nothing. Außerdem sehen Sie, dass der zweite Operand immer ausgewertet wird, wenn der erste Nothing ist (ein Kurzschluss würde in der Ausgabe ein And bzw. Or erzeugen).
Nothing AndAlso Nothing : Nothing
Nothing OrElse Nothing : Nothing
Nothing AndAlso True : Nothing
Nothing OrElse True : True
Nothing AndAlso False : False
Nothing OrElse False : Nothing
True AndAlso Nothing : Nothing
True Or Nothing : True
False And Nothing : False
False OrElse Nothing : Nothing
4.5.3 Boxing 

Bei der Weitergabe oder Zuweisung eines nullbaren Typs an eine Referenzvariable (Object, ValueType oder eine implementierte Schnittstelle) können zwei Fälle unterschieden werden:
- Wert ist Nothing: Die Referenz wird selbst auf Nothing gesetzt.
- Es liegt ein Wert vor: Die Referenzvariable erhält den übergebenen Wert (nicht nullbar).
Das folgende Codefragment zeigt diesen Mechanismus:
'...\Datentypen\Nullable\Boxing.vb |
Option Strict On
Namespace Datentypen
Module Boxing
Sub Test()
Dim zahl As Integer? = 125
Dim box As Object = zahl
Console.WriteLine("Referenztyp: {0}", box.GetType())
Console.WriteLine("Zahlenwert: {0}", box)
zahl = Nothing
box = zahl
Console.WriteLine("Referenz ist Nothing: {0}", box Is Nothing)
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt keinen nullbaren Typ und einen Wert von Nothing für die Referenz auf einen nullbaren Typ mit dem Wert Nothing.
Referenztyp: System.Int32
Zahlenwert: 125
Referenz ist Nothing: True
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.