Die Community zu .NET und Classic VB.
Menü

Datentypen für Datum und Zeit mit Unterstützung für Nullwerte

 von 

Einleitung 

Die Klassenbibliothek des .NET Frameworks stellt den Strukturdatentyp DateTime (in Visual Basic .NET Date) zur Speicherung und Manipulation von Datums- und Zeitangaben zur Verfügung. Dieser Datentyp besitzt jedoch den Nachteil, dass er nicht in der Lage ist, Nullwerte aufzunehmen. Nullwerte zeigen das Fehlen eines gültigen Wertes an. In der Praxis kommt es oft vor, dass Datumsangaben nicht bekannt sind, etwa die Geburtsdaten von Mitarbeitern oder Zeitpunkte, an denen Ereignisse stattfinden. Während die Speicherung von Nullwerten in Datenbanken kein Problem darstellt, muss im Programmcode anstelle von DateTime ein Datentyp benutzt werden, der auch Nullwerte aufnehmen kann. Im Folgenden werden mehrere Möglichkeiten zur Unterstützung von Nullwerten bei Datumsangaben betrachtet.

Hinweis:
Die Erstveröffentlichung dieses Tutorials finden Sie unter http://dotnet.mvps.org/dotnet/articles/nullabledates/

Boolesche Variablen zur Anzeige von Nullwerten  

Die einfachste, aber zugleich aufwendigste Möglichkeit zur Kennzeichnung von Nullwerten ist das Mitführen einer booleschen Variablen, die anzeigt, ob der in einer Variablen des Typs DateTime gespeicherte Datumswert gültig ist oder nicht. Nachteilig an dieser Lösung ist, dass Datumsvariable und boolesche Variable nicht fest aneinander gebunden sind, obwohl sie eine Entität darstellen. Quellcode wird so schnell in seiner Wartbarkeit reduziert, insbesondere dann, wenn mehrere Datumswerte verwaltet werden müssen. Folgendes Listing zeigt ein Beispiel für die Implementierung dieser Methode.

Private m_Birthday As Date
Private m_BirthdayIsNull As Boolean
.
.
.
If m_BirthdayIsNull Then
    Me.LabelBirthday.Text = "(unbekannt)"
Else
    Me.LabelBirthday.Text = CStr(m_Birthday)
End If

Listing 1: Nullwerte mit zusätzlichen Booleans kennzeichnen

Interpretation eines bestimmten Datumswerts als Nullwert  

Eine weitere Lösung, die häufig genannt wird, liegt darin, einen bestimmten, fest gewählten, im Datentyp DateTime darstellbaren Datumswert als Nullwert zu interpretieren. Meist stellen dabei die Datumswerte DateTime.MinValue oder DateTime.MaxValue den Nullwert dar. Der Vorteil dieser Lösung ist die Einfachheit der Implementierung, so werden keine zusätzlichen Variablen oder Klassen benötigt. Nachteilig erweist sich jedoch, dass ein gültiger Datumswert zur Darstellung eines ungültigen Wertes herangezogen wird. Das ist etwa dann problematisch, wenn ein derartiger Datumswert in der Objektschnittstelle einer wiederverwendbaren Bibliothek enthalten und die spezielle Bedeutung eines bestimmten Wertes des Typs DateTime nicht für diesen Datentyp dokumentiert ist. Zudem erlauben Steuerelemente wie das DateTimePicker-Steuerelement die Auswahl des als Nullwert interpretierten Datumswerts, sofern der Auswahlbereich nicht eingeschränkt wurde.

Private m_Birthday As Date
.
.
.
If m_Birthday = Date.MinValue Then
    Me.LabelBirthday.Text = "(unbekannt)"
Else
    Me.LabelBirthday.Text = CStr(m_Birthday)
End If

Listing 2: Festlegung eines bestimmten Werts als Nullwert

Kapseln und erweitern des Datentyps DateTime  

Während die ersten beiden vorgestellten Möglichkeiten wenig praxistauglich sind und nur der Vollständigkeit halber in diesen Artikel aufgenommen wurden, beschreiben die folgenden Abschnitte Lösungswege, die sich in verschiedenen Szenarien als vorteilhaft erweisen. Die Idee hinter dieser Methode ist, den Datentyp DateTime um eine Eigenschaft zu erweitern, die anzeigt, ob das Objekt einen Nullwert enthält oder nicht. Der Datentyp DateTime ist jedoch als Strukturtyp implementiert und kann deshalb nicht durch Vererbung erweitert werden. Stattdessen wird ein neuer Datentyp NullableDateTime erstellt, der, in Anlehnung an die Implementierung des Typs DBNull, in der Eigenschaft Value den Datumswert aufnimmt und über den Wert der Eigenschaft IsNull einen Nullwert anzeigt. Je nach gewünschter Semantik kann NullableDateTime als Struktur- oder Klassentyp implementiert werden.

Public Structure NullableDateTime
    Private m_Value As Date
    Private m_IsNull As Boolean
    
    Public Property Value() As Date
        Get
            Return m_Value
        End Get
        Set(ByVal Value As Date)
            m_Value = Value
        End Set
    End Property
    
    Public Property IsNull() As Boolean
        Get
            Return m_IsNull
        End Get
        Set(ByVal Value As Boolean)
            m_IsNull = Value
        End Set
    End Property
End Structure

Listing 3: Verwendung eines eigenen Typs als Kapselung

In Visual Basic .NET besitzt der Datentyp DateTime die Parallelbezeichnung Date. Dementsprechend wäre es wünschenswert, in Visual Basic .NET den oben vorgestellten Datentyp NullableDateTime mit dem Namen NullableDate ansprechen zu können, um Konsistenz in der Benennung der Datentypen zu wahren. Die Imports-Direktive der Programmiersprache Visual Basic .NET erweist sich zu diesem Zweck als nützlich, erlaubt sie es doch, Namen von Namensräumen und Klassen Parallelbezeichnungen zuzuordnen. Unter der Annahme, dass der Datentyp NullableDateTime Mitglied des Namensraums MvpsOrg.DotNet.NullableTypes ist, reicht es aus, in den jeweiligen Codedateien vor der ersten Deklaration die Zeile Imports NullableDate = MvpsOrg.DotNet.NullableTypes.NullableDateTime aufzunehmen, um NullableDate direkt im Quellcode der Datei benutzen zu können. Nachstehendes Listing gibt ein Beispiel für die Verwendung des Datentyps NullableDate.

Imports NullableDate = MvpsOrg.DotNet.NullableTypes.NullableDateTime

Private m_Birthday As NullableDate
.
.
.
If m_Birthday.IsNull Then
    Me.LabelBirthday.Text = "(unbekannt)"
Else
    Me.LabelBirthday.Text = CStr(m_Birthday.Value)
End If

Listing 4: Verwendung eines eigenen Typs als Kapselung

Nutzung des Datentyps SqlDateTime  

Neu in .NET 2.0 ist die Unterstützung für Generizität. Generizität ermöglicht es, Klassen mit Typparametern auszustatten, in denen der Benutzer der Klasse den Typ von Methodenparametern, -rückgabewerten und Eigenschaften angeben kann. In der Klassenbibliothek des .NET Frameworks finden sich einige vordefinierte nützliche generische Klassen, darunter Nullable(Of T) im Namensraum System. Diese Klasse ermöglicht es, Datentypen um die Fähigkeit Nullwerte aufzunehmen, zu erweitern. Die wichtigsten Eigenschaften von Nullable(Of T) sind die Eigenschaft Value, deren Datentyp den im Typparameter T übergebenen Typ annimmt, sowie die boolesche Eigenschaft HasValue, die mit der Eigenschaft IsNull von SqlDateTime vergleichbar ist und deren Wert ebenfalls Aufschluss darüber gibt, ob das Objekt einen gültigen Wert enthält. Nachstehendes Listing zeigt ein Beispiel für die Verwendung von Nullable(Of T) zur Darstellung nullierbarer Datumsangaben.

Private m_Birthday As Nullable(Of Date)
.
.
.
If m_Birthday.HasValue Then
    Me.LabelBirthday.Text = CStr(m_Birthday.Value)
Else
    Me.LabelBirthday.Text = "(unbekannt)"
End If

Listing 5: Verwendung eines eigenen Typs als Kapselung

Zur Vereinfachung der Verwendung von Nullable(Of Date) bietet sich die Einführung einer Parallelbezeichnung an. Anschliessend kann im Code komfortabel mit NullableDate auf den Datentyp Nullable(Of Date) verwiesen werden. Das Erben von Nullable(Of Date) zur Einführung eines neuen Typnamens ist nicht möglich, da es sich bei Nullable(Of Date) wie bei DateTime um einen Strukturtyp handelt. Da die Eigenschaft Value von Nullable(Of Date) den Datentyp DateTime besitzt, kann von den für DateTime definierten Operatoren Gebrauch gemacht werden.

Imports NullableDate = System.Nullable(Of Date)

Private m_Birthday As NullableDate
.
.
.
If m_Birthday.HasValue Then
    Me.LabelBirthday.Text = CStr(m_Birthday.Value)
Else
    Me.LabelBirthday.Text = "(unbekannt)"
End If

Listing 6: Verwendung eines eigenen Typs als Kapselung

Schlusswort  

Erst ab .NET 2.0 ist mit der generischen Klasse Nullable(Of T) der Umgang mit Nullwerten bei Datumsangaben auf komfortable Art und Weise möglich, sodass man nicht mehr auf einen selbst geschriebenen Ersatz oder gar das automatisierte Erstellen nullierbarer Datentypen mittels CodeDom zur Laufzeit angewiesen ist. Dennoch stellt Nullable(Of Date) weiterhin nicht in allen Fällen die beste Lösung dar. Bei der Arbeit mit Datenbanken wird es weiterhin von Vorteil sein, den Datentyp SqlDateTime zu benutzen. Es bleibt zu hoffen, dass die vielen verschiedenen Eigenbauten zur Simulation nullierbarer Datumsangaben nach und nach durch Nullable(Of Date) ersetzt werden und damit die Wiederverwendbarkeit von Code erhöht wird. C# 2.0 enthält bereits syntaktische Unterstützung für den Umgang mit nullierbaren Datentypen, andere .NET-Programmiersprachen werden folgen.

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.