4.3 Enumerationen 

»Weniger ist mehr.« (Ludwig Mies van der Rohe)
Wenn eine Variable nur eine begrenzte Anzahl an Werten annehmen kann, sollte verhindert werden, dass »falsche« Werte zugewiesen werden können. Eine Prüfung vor jeder Neubelegung ist sehr unkomfortabel und fehlerträchtig. Besser ist es, wenn aufgrund des Datentyps der Variablen lediglich erlaubte Werte zugewiesen werden können. Ein erster Ansatz dazu stützt sich auf die Definition einer Klasse.
Class Farbe
Public Const Kreuz As Integer = 3
Public Const Pik As Integer = 2
Public Const Herz As Integer = 1
Public Const Karo As Integer = 0
End Class
Wenn Sie nun eine Variable vom Typ Farbe definieren, können Sie auf diese Konstanten zugreifen. Obwohl das das Problem im Prinzip löst, ist es keine ideale Lösung. Einerseits ist es relativ viel Tipparbeit für ein paar Werte, und andererseits fehlen alle Funktionen, die mit den Konstanten zusammenhängen, wie etwa die Repräsentation als Zeichenkette oder die Aufzählung aller erlaubten Konstanten. Außerdem wird nicht verhindert, dass inkompatible oder bezüglich eines Vergleichs ungeeignete Typen verwendet werden (zum Vergleichsproblem siehe den Unterabschnitt »Fließkommazahlen als Zähler« in Abschnitt 2.10.2, »For«). Daher bietet Visual Basic einen Datentyp, der sich auf das Wesentliche beschränkt, mit folgender Syntax (optionale Teile stehen in eckigen Klammern, kursiv gesetzte Teile müssen Sie Ihren Erfordernissen anpassen):
[<Modifikatoren>] Enum Enumeration [As Typ] |
Dabei sind noch ein paar Besonderheiten zu beachten:
- Als Modifikatoren sind nur Public und Friend erlaubt sowie Shadows, Private und Protected für Enumerationen als Teil anderer Datentypen.
- Typ darf nur einer der ganzzahligen Primitiven Byte, SByte, UShort, UInt16, Short, Int16, UInteger, UInt32, Integer, Int32, ULong, UInt64, Long oder Int64 sein. Ohne Angabe der As-Klausel ist es Integer.
- Jedes Mitglied steht in einer eigenen logischen Zeile (logische Zeilen können mit einem Doppelpunkt zu einer Codezeile zusammengefasst werden, siehe Abschnitt 2.2.4, »Anweisungen«).
- Mitglieder einer Enumeration haben keinerlei Modifikatoren, sondern sie sind implizit Public Const und werden damit über den Klassennamen angesprochen.
- Name muss innerhalb der Enumeration eindeutig sein.
- Konstanten müssen zur Compilezeit bekannt sein und zu Typ passen.
- Mitglieder ohne Initialisierung sind um eins größer als ihre Vorgänger, wobei eine fehlende Initialisierung des ersten Mitglieds durch 0 ersetzt wird.
- Mitglieder können nur als Konstanten nur für nachfolgende Mitglieder verwendet werden.
- Eine Konstante darf kleiner sein als das vorherige Mitglied.
- Eumerationen können weder mit Implements eine Schnittstelle implementieren noch mit Inherits eine Elternklasse festlegen.
- Andere Arten von Mitgliedern sind verboten, auch ein Klassenkonstruktor.
- Wie Primitive sind Enumerationswerte unveränderlich.
Als Enumeration ist obige Klasse kürzer formulierbar:
Enum Farbe
Karo
Herz
Pik
Kreuz
End Enum
Oder noch kürzer (auch wenn Visual Studio beim Tippen immer wieder versucht, End Enum einzufügen):
Enum Farbe : Karo : Herz : Pik : Kreuz : End Enum
Das folgende Codefragment definiert dieselbe Enumeration auf eine etwas andere Art und gibt in einer Schleife die Werte aus – zusammen mit einem Vergleich mit dem festen Wert pik. Zum Test enthält die Enumeration drei Mitglieder mit gleichem Wert.
'...\Datentypen\Enumerationen\Prinzip.vb |
Option Strict On
Namespace Datentypen
Enum Farbe
Karo
Pik = 2
Kreuz
Herz = Karo + 1
Eichel = Kreuz
Treff = Kreuz
End Enum
Module Prinzip
Sub Test()
Dim pik As Farbe = Farbe.Pik
For Each f As Farbe In [Enum].GetValues(GetType(Farbe))
Console.WriteLine("Farbe {0} mit Wert {1} gleich Pik: {2}", _
f, CType(f, Integer), pik = f)
Next
Console.ReadLine()
End Sub
End Module
End Namespace
Die von WriteLine implizit aufgerufene Methode ToString() erzeugt die von uns spezifizierten Namen. Bei mehrdeutigen Werten wird der bezüglich des aus dem Namen gebildeten Hashwerts erste Name genommen, hier der Name Eichel. Die standardmäßige Ausgabe von Namen statt Werten ist der Grund für die Typumwandlung mit CType. Durch sie wird ein String von der ganzen Zahl und nicht dem Enumerationsmitglied erzeugt.
Farbe Karo mit Wert 0 gleich Pik: False
Farbe Herz mit Wert 1 gleich Pik: False
Farbe Pik mit Wert 2 gleich Pik: True
Farbe Eichel mit Wert 3 gleich Pik: False
Farbe Eichel mit Wert 3 gleich Pik: False
Farbe Eichel mit Wert 3 gleich Pik: False
Hinweis |
Wie alle Werttypen wird keine Referenz erzeugt, sondern die Werte werden direkt in dem Block gespeichert, in dem die Enumerationsvariable deklariert ist. Zu den sich daraus ergebenden Effekten vergleiche Abschnitt 4.2.1, »Vor- und Nachteile von Referenz- und Werttypen«, und Abschnitt 4.2.3, »Boxing«. |
4.3.1 Datentyp 

Wenn Sie für eine kleinere Anzahl Konstanten nicht 4 Byte einer Integer-Variablen verbrauchen möchten oder die Werte der Konstanten nicht zu Integer passen, können Sie den Datentyp der Enumeration explizit festlegen. Das folgende Codefragment zeigt dazu zwei Enumerationen mit unterschiedlichem ganzzahligem Typ. Die Werte der ersten sind so gewählt, dass sie jeweils nur ein Bit setzen und als Flags dienen können, die ohne Informationsverlust kombiniert werden können. In der Methode Test() wird mit den Enumerationswerten gerechnet, und sie werden wie jede andere ganze Zahl konvertiert:
'...\Datentypen\Enumerationen\Datentyp.vb |
Option Strict On
Namespace Datentypen
Enum Font As Byte
Kursiv = 1
Fett = 2
Hervorgeboben = Kursiv Or Fett
Unterstrichen = 4
End Enum
Enum Einheit As Long
Meter = 1
Erddurchmesser = 12756000
AstronomischeEinheit = 149597870691L
Parsec = 30856775812743679L
End Enum
Module Datentyp
Sub Test()
Dim art As Font = Font.Unterstrichen Or Font.Kursiv
Dim typ As Integer = art
Console.Write("Die Schrift ist ")
For Each f As Font In [Enum].GetValues(GetType(Font))
If (typ And f) = f Then Console.Write(f.ToString() & " ")
Next
Console.WriteLine("({0}, nicht {1})", art, Font.Fett Or Font.Kursiv)
Console.WriteLine("{0} Erddurchmesser bis zur Sonne", _
Einheit.AstronomischeEinheit \ Einheit.Erddurchmesser)
Console.WriteLine("Nächste Stern: {0}-mal weiter weg als die Sonne", _
CType(1.33 * Einheit.Parsec / Einheit.AstronomischeEinheit, Integer))
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe bestätigt die korrekte Arbeitsweise. Außerdem ist in der ersten Zeile zu sehen, dass bei einer Kombination von Enumerationswerten die automatische Konvertierung zu den Namen in der Enumeration verloren geht, wenn für den Zahlenwert der Kombination kein Name in der Enumeration vergeben ist .
Die Schrift ist Kursiv Unterstrichen (5, nicht Hervorgeboben)
11727 Erddurchmesser bis zur Sonne
Nächste Stern: 274332-mal weiter weg als die Sonne
Enumerationen können auch als Datentyp für Methodenparameter dienen. Das nächste Beispiel nutzt eine Enumeration als Parameter der Methode Info(), da es nur exakt 5 Körper gibt, die von gleichen regelmäßigen Vielecken begrenzt sind.
'...\Datentypen\Enumerationen\Parameter.vb |
Option Strict On
Namespace Datentypen
Enum Platonisch
: Tetraeder : Würfel : Oktaeder : Dodekaeder : Ikosaeder
End Enum
Module Parameter
Sub Info(ByVal körper As Platonisch)
Dim f As String = "{0,10}: {1,2} Ecken, {2,2} Kanten, {3,2} Flächen"
Select Case körper
Case Platonisch.Tetraeder : Console.WriteLine(f, körper, 4, 6, 4)
Case Platonisch.Würfel : Console.WriteLine(f, körper, 8, 12, 6)
Case Platonisch.Oktaeder : Console.WriteLine(f, körper, 6, 12, 8)
Case Platonisch.Dodekaeder : Console.WriteLine(f,körper, 20, 30, 12)
Case Platonisch.Ikosaeder : Console.WriteLine(f, körper, 12, 30, 20)
End Select
End Sub
Sub Test()
For Each p As Platonisch In [Enum].GetValues(GetType(Platonisch))
Info(p)
Next
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt einige Eigenschaften aller vollständig regelmäßigen Körper:
Tetraeder: 4 Ecken, 6 Kanten, 4 Flächen
Würfel: 8 Ecken, 12 Kanten, 6 Flächen
Oktaeder: 6 Ecken, 12 Kanten, 8 Flächen
Dodekaeder: 20 Ecken, 30 Kanten, 12 Flächen
Ikosaeder: 12 Ecken, 30 Kanten, 20 Flächen
4.3.2 System.Enum 

Alle Enumerationen sind von System.Enum abgeleitet. Dies wird vom Compiler erledigt, Sie dürfen eine entsprechende Inherits-Klausel nicht angeben. Nehmen wir ein einfaches Beispiel:
Enum Enumeration As Long
Mitglied = 17
End Enum
Der Compiler passt die Enumeration an und stellt die Vererbungsbeziehung her. Im folgenden Listing sind die Signaturen abgedruckt. System.Enum ist von System.ValueType abgeleitet, das wiederum in Abschnitt 4.2.5, »ValueType«, abgedruckt ist. Als obsolet markierte Mitglieder sind in spitze Klammern gesetzt, vor implementierenden Methoden steht, durch einen Punkt getrennt, die Schnittstelle. Um die Ausgabe nicht zu lang werden zu lassen, sind alle ByVal-Modifikatoren von Methodenparametern sowie die Modifikatoren NotOverridable, Overrides und Overloads ausgelassen. Optionale Parameter sind kursiv gesetzt. Der Name GANZ steht für Byte, SByte, UShort, UInt16, Short, Int16, UInteger, UInt32, Integer, Int32, ULong, UInt64, Long oder Int64, PRIM darf zusätzlich für Boolean, Char, Single, Double oder DateTime stehen. Der Parameter ET ist der Datentyp der Enumeration, W der betrachtete Wert. Die privaten Mitglieder sind der Vollständigkeit halber abgedruckt und nur über Klassen aus dem Namensraum Reflection verwendbar.
Public NotInheritable Class Enumeration : Inherits Enum
Public value__ As Long
Public Shared Const Mitglied As Enumeration
End Class
Public MustInherit Class Enum Inherits ValueType
'Public
Function Equals(obj As Object) As Boolean
Function GetHashCode() As Integer
Function ToString() As String
Function ToString(format As String) As String
Function <ToString>(format As String, konv As IFormatProvider) As String
Function IComparable.CompareTo(mit As Object) As Integer
Function IConvertible.GetTypeCode() As TypeCode
Shared Function Parse(ET As Type, W As String, egal As Boolean) As Object
Shared Function GetUnderlyingType(ET As Type) As Type
Shared Function GetValues(ET As Type) As Array
Shared Function GetName(ET As Type, W As Object) As String
Shared Function GetNames(ET As Type) As String()
Shared Function IsDefined(ET As Type, W As Object) As Boolean
Shared Function Format(ET As Type, W As Object, form As String) As String
Shared Function ToObject(ET As Type, W As Object) As Object
Shared Function ToObject(ET As Type, W As GANZ) As Object
'Private
Function IConvertible.ToPRIM(konv As IFormatProvider) As PRIM
Function Iconvertible.ToType(ET As Type,konv As IFormatProvider) As Object
Shared Function InternalCompareTo(o1 As Object, o2 As Object) As Integer
Shared Function InternalGetUnderlyingType(ET As Type) As Type
Shared Sub InternalGetEnumValues( _
ET As Type, ByRef werte As ULong(), ByRef namen As String())
Shared Function InternalBoxEnum(ET As Type, W As Long) As Object
Function InternalGetValue() As Object
Shared Function GetValueField(ET As Type) As FieldInfo
Shared Function GetHashEntry(ET As Type) As HashEntry
Shared Function InternalGetValueAsString(ET As Type,W As Object) As String
Shared Function InternalFormattedHexString(W As Object) As String
Shared Function InternalFormat(ET As Type, W As Object) As String
Shared Function InternalFlagsFormat(ET As Type, W As Object) As String
Shared Function ToUInt64(W As Object) As ULong
Shared Function BinarySearch(array As ULong(), W As ULong) As Integer
Function GetValue() As Object
Function ToHexString() As String
Shared enumSeperatorCharArray As Char()
Shared intType As Type
Shared stringType As Type
Shared fieldInfoHash As Hashtable
Shared Const enumSeperator As String
Shared Const maxHashElements As Integer
Class HashEntry Inherits Object
Public names As String()
Public values As ULong()
End Class
End Enum
Daraus können Sie erkennen, dass die Elternklasse aller Enumerationen selbst ein Referenztyp ist (so merkwürdig das auch scheinen mag).
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.