Arbeiten mit .NET: C-Sharp/ Grundlagen/ Datentypen/ Aufzählungen

Aus Wikibooks
Zur Navigation springen Zur Suche springen

Problem[Bearbeiten]

Stellen wir uns vor, wir würden ein Programm schreiben, das zahlreiche Daten immer und immer wieder benutzt, um bestimmte konstante Werte zu beschreiben:

 class Zeichenbrett
 {
   private const int c_Transparent = 0;
   private const int c_Rot = 1;
   private const int c_Gelb = 2;
   private const int c_Gruen = 3;
   private const int c_Blau = 4;
   private const int c_HellGrau = 5;
   private const int c_Grau = 6;
   private const int c_Schwarz = 7;
   private const int c_Weiss = 8;
  
   public void LoescheZeichenflaeche()
   {
     FuelleZeichenFlaeche( c_Weiss ); // Farbwert 8 übergeben
   }
 }

Bis hierhin wäre es ja noch verständlich und nachvollziehbar, aber was passiert, wenn wir diese Bibliothek abgeschlossen haben und uns – Wochen oder Monate später – einer Erweiterung widmen?

 class Pinsel
 {
   private const int c_Transparent = 0;
   private const int c_Schwarz = 1;
   private const int c_Weiss = 2;
   private const int c_Gelb = 3;
   private const int c_Rot = 4;
   private const int c_Blau = 5;
   private const int c_Gruen = 6;
   private const int c_HellGrau = 7;
   private const int c_Grau = 8;
 
   public void ZeichneLinie()
   {
     SetzePinselFarbe ( c_Weiss ); // Farbwert 2 übergeben
   }
 }

Dieses Beispiel funktioniert wunderbar. Bis eines Tages ein Fehler auftritt. Zur Fehlersuche müssten wir uns nun nämlich zwei verschiedene Werte für jede Farbe merken. Natürlich neben all den Variablennamen, Klassennamen, Methodennamen, Zuständen und dem ganzen anderen Kram.

Wäre es da nicht ein Segen, könnte man hier für alle Klassen einheitliche und zentral festgelegte Werte verwenden?

Enum[Bearbeiten]

Aufzählungen, also Enumerationen, oder eben enums, werden verwendet, um einen Satz an „benannten Werten“ festzulegen, also Zahlen sozusagen Namen zuzuweisen. Und sie eignen sich hervorragend, Programmcode lesbarer zu machen: Einmal beschrieben,

 enum Farbe 
 {
   Transparent = 0,
   Schwarz = 1,
   Weiss = 2,
   Gelb = 3,
   Rot = 4,
   Blau = 5,
   Gruen = 6,
   HellGrau = 7,
   Grau = 8
 }

lassen sie sich beliebig oft einsetzen:

 class Zeichenbrett
 {
   public void LoescheZeichenflaeche()
   {
     FuelleZeichenFlaeche( Farbe.Weiss ); // Farbwert 2 übergeben
   }
 }
 
 class Pinsel
 {
   public void ZeichneLinie()
   {
     SetzePinselFarbe ( Farbe.Weiss );   // Farbwert 2 übergeben
   }
 }

Natürlich lassen sich die Zahlenwerte jederzeit anpassen oder die enum-Liste bei Bedarf an zentraler Stelle erweitern. Und selbstverständlich sind sie nicht nur auf Farben begrenzt:

  enum Schalter
  {
    aus,
    ein
  }

  enum Woche : int
  {
    Montag = 1,
    Dienstag,
    Mittwoch,
    Donnerstag,
    Freitag,
    Samstag = 6,
    Sonnabend = 6,
    Sonntag
  }

Die enum-Liste Schalter hat nun einen 0-basierten Index, der, wenn nichts anders angegeben ist, als Standard angenommen wird. aus entspricht also dem Zahlenwert 0, während ein den Zahlenwert 1 hat.

Unsere Woche wiederum weist einen 1-basierten Index aus, weil der erste Wert mit 1 beginnt, und alle nachfolgenden, soweit nicht ausdrücklich zugeordnet, jeweils fortlaufend weitergezählt werden. Zudem deklariert die Woche ausdrücklich, dass alle Mitglieder dieser Aufzählung vom Datentyp int sind. Und schließlich gibt es für den Zahlenwert 6 zwei verschiedene Schreibweisen: Sowohl Samstag, als auch Sonnabend sind also erlaubt und führen zum gleichen Ergebnis.

Probieren wir es am besten gleich aus, denn nur der Versuch macht kluch!

C#-Quelltext
using System;
 
namespace Org.Wikibooks.De.CSharp.Enums
{
   enum Woche :  int
   {
     Montag = 1,
     Dienstag,
     Mittwoch,
     Donnerstag,
     Freitag,
     Samstag = 6,
     Sonnabend = 6,
     Sonntag
   }
   
   class Program
   {
     static void Main(string[] args)
     {
       Console.WriteLine( "Montag hat die Zuordnung: {0}", (int)Woche.Montag );
       Console.WriteLine( "Samstag hat die Zuordnung: {0}", (int)Woche.Samstag );
       Console.WriteLine( "Sonnabend hat die Zuordnung: {0}", (int)Woche.Sonnabend );
 
       Console.WriteLine( "Der dritte Tag der Woche heißt {0}", ((Woche)3).ToString() );
 
       // Wir warten auf [ENTER]
       Console.ReadLine();
     }
   }
}
Crystal Clear app terminal.png Ausgabe
Montag hat die Zuordnung: 1
Samstag hat die Zuordnung: 6
Sonnabend hat die Zuordnung: 6
Der dritte Tag der Woche heißt Mittwoch

Durch die explizite Typumwandlung in der ersten Zeile erreichen wir den int-Zahlenwert, der durch Woche.Montag repräsentiert wird. Und auch Woche.Samstag und Woche.Sonnabend haben erwartungsgemäß die Zahl 6 zurückgeliefert.

In der vierten Zeile hingegen drehen wir die Typumwandlung um. Und siehe da, auch das funktioniert. Indem wir int 3 nach Woche umwandeln, können wir uns sogar den Namen des durch die 3 repräsentierten Tages der Woche ausgeben lassen. Einfacher wäre es natürlich gewesen, hätten wir

Console.WriteLine( Woche.Mittwoch.ToString() );

geschrieben. Aber einfach ist manchmal langweilig. Und irgendwie muss man ja wenigstens versuchen, den Schein aufrecht zu erhalten, das Programmieren mit C# sei wahnsinnig schwierig, kompliziert und unverständlich kryptisch.

Bitmanipulation[Bearbeiten]

Aufzählungen sind aber nicht nur zur Sammlung von Konstanten praktisch. Sie können auch als Sammlung sogenannter Flags herhalten. Dazu muss man sie nur mit dem gleichnamigen Attribut versehen:

 [Flags]
 enum Optionen
 {
   Lesen = 0x01,     // entspricht: 00000001
   Schreiben = 0x02, // entspricht: 00000010
   Oeffnen = 0x04,   // entspricht: 00000100
   Drucken = 0x08    // entspricht: 00001000
 }

Tipp: Für Flags ist es sinnvoll, sie als Bitmuster zu betrachten, um Redundanzen auszuschließen. Würden wir hier für Oeffnen den Wert 0x03 (bitweise also 00000011) vergeben, wäre das die Kombination aus Lesen und Schreiben. Am Ende wüssten wir nicht mehr, ob der Benutzer die Datei nun gerade Lesen und Schreiben oder doch nur Öffnen darf. Für die Bitmuster eignet sich übrigens die hexadezimale Schreibweise (0x00 - 0xFF) am besten, ist aber nicht zwingend notwendig.
Wie auch immer – nun können wir diese Flags jedenfalls wunderbar kombinieren:

C#-Quelltext
using System;
 
namespace Org.Wikibooks.De.CSharp.Enums
{
   [Flags]
   enum Optionen
   {
     Lesen = 0x01,     // 0000 0 0 0 1
     Schreiben = 0x02, // 0000 0 0 1 0
     Oeffnen = 0x04,   // 0000 0 1 0 0
     Drucken = 0x08    // 0000 1 0 0 0
   }
 
   class Program
   {
     static void Main(string[] args)
     {
       // Wir kombinieren "Öffnen" und "Drucken".
       Optionen erlaubterZugriff = Optionen.Oeffnen | Optionen.Drucken;
       // Das |-Zeichen ist übrigens 
       // der Operator "bitweises Oder".
       // Von rechts nach links werden also die untereinander gestellten Bits verglichen
       // und, wenn das obere ODER das untere Bit 1 ist, als 1;
       // ansonsten als 0 übernommen.
       //       0000 0 1 0 0 (Oeffnen)
       //       0000 1 0 0 0 (Drucken)
       //     ==============
       //     = 0000 1 1 0 0 = 0x0C = 12
 
       Console.WriteLine( "Erlaubter Zugriff: {0}", erlaubterZugriff.ToString() );
       Console.WriteLine( "Zahlenwert: {0}; also {1} | {2}"
                          ,(int)erlaubterZugriff 
                          ,(int)Optionen.Drucken
                          ,(int)Optionen.Oeffnen ); 
 
       // Wir warten auf [ENTER]
       Console.ReadLine();
     }
   }
}


Siehe auch[Bearbeiten]