Datentypen (C# und Java im Vergleich)

Aktualisiert: November 2007

In diesem Thema werden einige der wichtigsten Ähnlichkeiten und Unterschiede in Bezug auf die Darstellung, Zuordnung und Garbage Collection von Daten zwischen Java und C# beschrieben.

Zusammengesetzte Datentypen

Das Konzept der Klasse als zusammengesetzter Datentyp mit Feldern, Methoden und Events ist in Java und C# ähnlich. (Die Klassenvererbung wird im Thema Vererbung und abgeleitete Klassen (C# und Java im Vergleich) separat behandelt.) C# führt das Konzept von Strukturen als stapelzugeordneter zusammengesetzter Datentyp ein, der Vererbung nicht unterstützt. In sonstiger Hinsicht sind sich Strukturen und Klassen sehr ähnlich. Strukturen bieten eine vereinfachte Möglichkeit zum Gruppieren von zusammengehörigen Feldern und Methoden zur Verwendung in geschlossenen Schleifen und sonstigen Szenarios, in denen Leistung ein entscheidender Faktor ist.

Mit C# können Sie eine Destruktormethode erstellen, die aufgerufen wird, bevor Instanzen einer Klasse in die Garbage Collection aufgenommen werden. In Java kann eine finalize-Methode verwendet werden. Diese enthält Code, der Ressourcen bereinigt, bevor das Objekt der Garbage Collection übergeben wird. In C# wird diese Funktion vom Klassendestruktor ausgeführt. Der Destruktor ähnelt einem Konstruktor ohne Argumente und mit vorangehender Tilde (~).

Integrierte Datentypen

C# stellt alle in Java verfügbaren Datentypen bereit. Zusätzlich bietet es Unterstützung für Zahlen ohne Vorzeichen und einen 128-Bit-Gleitkommatyp mit hoher Genauigkeit.

Die Kernklassenbibliothek stellt für jeden primitiven Datentyp eine Wrapperklasse bereit, die ihn als Java-Objekt darstellt. Beispielsweise umschließt die Int32-Klasse den int-Datentyp und die Double-Klasse den double-Datentyp.

Andererseits sind in C# alle primitiven Datentypen Objekte im System-Namespace. Für jeden Datentyp wird ein kurzer Name oder Alias bereitgestellt. Beispielsweise ist int der kurze Name für System.Int32, und double ist der kurze Name von System.Double.

Eine Liste mit C#-Datentypen und ihren Aliasen wird in der folgenden Tabelle bereitgestellt. Wie Sie sehen können, entsprechen die ersten acht Datentypen den primitiven Typen, die in Java verfügbar sind. Beachten Sie jedoch, dass boolean von Java in C# als bool bezeichnet wird.

Kurzer Name

.NET-Klasse

Typ

Breite

Bereich (Bits)

byte

Byte

Ganze Zahl ohne Vorzeichen

8

0 bis 255

sbyte

SByte

Ganze Zahl mit Vorzeichen

8

-128 bis 127

int

Int32

Ganze Zahl mit Vorzeichen

32

-2.147.483.648 bis 2.147.483.647

uint

UInt32

Ganze Zahl ohne Vorzeichen

32

0 bis 4294967295

short

Int16

Ganze Zahl mit Vorzeichen

16

-32.768 bis 32.767

ushort

UInt16

Ganze Zahl ohne Vorzeichen

16

0 bis 65535

long

Int64

Ganze Zahl mit Vorzeichen

64

-922337203685477508 bis 922337203685477507

ulong

UInt64

Ganze Zahl ohne Vorzeichen

64

0 bis 18446744073709551615

float

Single

Gleitkommatyp mit einfacher Genauigkeit

32

-3.402823e38 bis 3.402823e38

double

Double

Gleitkommatyp mit doppelter Genauigkeit

64

-1.79769313486232e308 bis 1.79769313486232e308

char

Char

Ein einzelnes Unicode-Zeichen

16

Im Text verwendete Unicode-Symbole

bool

Boolean

Logischer boolescher Typ

8

True oder False

object

Object

Basistyp aller anderen Typen

string

String

Eine Folge von Zeichen

decimal

Decimal

Präziser Bruchtyp oder ganzzahliger Typ, der Dezimalzahlen mit 29 signifikanten Stellen darstellen kann

128

±1,0 × 10e−28 zu ±7,9 × 10e28

Es ist möglich, eine Objektmethode für einen primitiven Datentyp aufzurufen, da C# alle primitiven Datentypen als Objekte darstellt. Beispiel:

static void Main()
{
    int i = 10;
    object o = i;
    System.Console.WriteLine(o.ToString());
}    

Dies wird mithilfe von automatischem Boxing und Unboxing erreicht. Weitere Informationen hierzu finden Sie unter Boxing und Unboxing (C#-Programmierhandbuch).

Konstanten

Sowohl Java als auch C# ermöglichen die Deklaration von Variablen, deren Wert zur Kompilierzeit angegeben wird und zur Laufzeit nicht mehr geändert werden kann. Zur Deklaration einer solchen Variablen werden in Java der final-Feldmodifizierer und in C# das const-Schlüsselwort verwendet. Neben const stellt C# das readonly-Schlüsselwort bereit, mit dem Variablen deklariert werden können, denen zur Laufzeit einmal ein Wert zugewiesen werden kann – entweder in der Deklarationsanweisung oder an anderer Stelle im Konstruktor. Nach der Initialisierung kann der Wert einer readonly-Variable nicht mehr geändert werden. Ein Szenario, in dem readonly-Variablen nützlich sind, liegt vor, wenn getrennt kompilierte Module Daten gemeinsam nutzen müssen (z. B. eine Versionsnummer). Wenn Modul A aktualisiert und mit einer neuen Versionsnummer erneut kompiliert wird, wird Modul B mit dem neuen konstanten Wert initialisiert und muss nicht erneut kompiliert werden.

Enumerationen

Mit Enumerationen (enums) können benannte Konstanten gruppiert werden, ähnlich der Verwendungsweise in C und C++. In Java sind sie nicht verfügbar. Im folgenden Beispiel wird eine einfache Color-Enumeration definiert.

public enum Color
{
    Green,   //defaults to 0
    Orange,  //defaults to 1
    Red,     //defaults to 2
    Blue     //defaults to 3
}  

Wie die folgende Enumerationsdeklaration zeigt, können Enumerationen auch ganzzahlige Werte zugewiesen werden:

public enum Color2
{
    Green = 10,
    Orange = 20,
    Red = 30,
    Blue = 40
}

Das folgende Codebeispiel ruft die GetNames-Methode des Enum-Typs auf, um die verfügbaren Konstanten für eine Enumeration anzuzeigen. Es weist einer Enumeration dann einen Wert zu und zeigt den Wert an.

class TestEnums
{
    static void Main()
    {
        System.Console.WriteLine("Possible color choices: ");

        //Enum.GetNames returns a string array of named constants for the enum.
        foreach(string s in System.Enum.GetNames(typeof(Color)))
        {
            System.Console.WriteLine(s);
        }

        Color favorite = Color.Blue;

        System.Console.WriteLine("Favorite Color is {0}", favorite);
        System.Console.WriteLine("Favorite Color value is {0}", (int) favorite);
    }
}

Ausgabe

Possible color choices:

Green

Orange

Red

Blue

Favorite Color is Blue

Favorite Color value is 3

Zeichenfolgen

Zeichenfolgentypen in Java und C# weisen ein ähnliches Verhalten mit leichten Unterschieden auf. Beide Zeichenfolgentypen sind unveränderlich. Das bedeutet, dass die Werte der Zeichenfolgen nach dem Erstellen nicht verändert werden können. In beiden Fällen erstellen Methoden, die scheinbar den eigentlichen Inhalt einer Zeichenfolge ändern, tatsächlich eine neue Zeichenfolge für die Rückgabe. Die ursprüngliche Zeichenfolge bleibt dabei unverändert. Die Vorgehensweise zum Vergleichen von Zeichenfolgenwerten ist in C# und Java verschieden. Um in Java Zeichenfolgenwerte vergleichen zu können, müssen Entwickler die equals-Methode für einen Zeichenfolgetyp aufrufen, da der Operator == standardmäßig Referenztypen vergleicht. In C# können Entwickler die Operatoren == oder != verwenden, um Zeichenfolgenwerte direkt zu vergleichen. Auch wenn in C# eine Zeichenfolge ein Referenztyp ist, vergleichen die Operatoren == und != standardmäßig die Werte der Zeichenfolge und nicht die Verweise.

Wie auch in Java sollten C#-Entwickler Zeichenfolgen nicht mithilfe von Zeichenfolgentypen verketten. Dadurch kann unnötiger Aufwand beim Erstellen neuer Zeichenfolgenklassen bei jeder Verkettung von Zeichenfolgen vermieden werden. Stattdessen können Entwickler die StringBuilder-Klasse verwenden, die funktionell der StringBuffer-Klasse in Java entspricht.

Zeichenfolgenliterale

Mit C# können Escapesequenzen wie "\t" für Tabstopps oder "\" für umgekehrte Schrägstriche innerhalb von Zeichenfolgenkonstanten vermieden werden. Dazu deklarieren Sie einfach die wörtliche Zeichenfolge mit dem @-Symbol, um diese der Zuweisung des Zeichenfolgenwerts voranzustellen. In den folgenden Beispielen wird die Verwendung von Escapezeichen und die Zuweisung von Zeichenfolgenliteralen veranschaulicht:

static void Main()
{
    //Using escaped characters:
    string path1 = "\\\\FileShare\\Directory\\file.txt";
    System.Console.WriteLine(path1);

    //Using String Literals:
    string path2 = @"\\FileShare\Directory\file.txt";
    System.Console.WriteLine(path2);
}

Konvertieren und Umwandeln

Sowohl Java als auch C# folgen ähnlichen Regeln für die automatische Konvertierung und die Umwandlung von Datentypen.

C# unterstützt wie Java sowohl implizite als auch explizite Typkonvertierungen. Im Fall von erweiternden Konvertierungen sind die Konvertierungen implizit. Zum Beispiel ist die folgende Konvertierung von int in long wie in Java implizit:

int int1 = 5;
long long1 = int1;  //implicit conversion

Im Folgenden finden Sie eine Liste mit impliziten Konvertierungen zwischen .NET Framework-Datentypen:

Quelltyp

Zieltyp

Byte

short, ushort, int, uint, long, ulong, float, double oder decimal

Sbyte

short, int, long, float, double oder decimal

Int

long, float, double oder decimal

Uint

long, ulong, float, double oder decimal

Short

int, long, float, double oder decimal

Ushort

int, uint, long, ulong, float, double oder decimal

Long

float, double oder decimal

Ulong

float, double oder decimal

Float

double

Char

ushort, int, uint, long, ulong, float, double oder decimal

Ausdrücke, die Sie explizit konvertieren möchten, werden mit der gleichen Syntax wie in Java umgewandelt:

long long2 = 5483;
int int2 = (int)long2;  //explicit conversion

In der folgenden Tabelle werden explizite Konvertierungen aufgelistet.

Quelltyp

Zieltyp

Byte

sbyte oder char

Sbyte

byte, ushort, uint, ulong oder char

Int

sbyte, byte, short, ushort, uint, ulong oder char

Uint

sbyte, byte, short, ushort, int oder char

Short

sbyte, byte, ushort, uint, ulong oder char

Ushort

sbyte, byte, short oder char

Long

sbyte, byte, short, ushort, int, uint, ulong oder char

Ulong

sbyte, byte, short, ushort, int, uint, long oder char

Float

sbyte, byte, short, ushort, int, uint, long, ulong, char oder decimal

Double

sbyte, byte, short, ushort, int, uint, long, ulong, char, float oder decimal

Char

sbyte, byte oder short

Decimal

sbyte, byte, short, ushort, int, uint, long, ulong, char, float oder double

Wert- und Referenztypen

C# unterstützt zwei Arten von Variablentypen:

  • Werttypen

    Dies sind die integrierten primitiven Datentypen, z. B. char, int und float, sowie benutzerdefinierte, mit struct deklarierte Typen.

  • Referenztypen

    Klassen und andere komplexe Datentypen, die aus den primitiven Typen erstellt werden. Variablen solcher Typen enthalten keine Instanz des Typs, sondern lediglich einen Verweis auf eine Instanz.

Wenn Sie die zwei Werttypvariablen i und j wie folgt erstellen, sind i und j voneinander komplett unabhängig:

int i = 10;
int j = 20;

Ihnen werden separate Speicheradressen zugewiesen:

Getrennte Speicheradressen für Werttypen

Wenn Sie den Wert einer dieser Variablen ändern, wird die andere davon natürlich nicht beeinflusst. Wenn beispielsweise ein Ausdruck wie der Folgende vorliegt, dann gibt es immer noch keine Verbindung zwischen den Variablen:

int k = i;

Wenn Sie den Wert von i ändern, bleibt der Wert von k derselbe, den i zum Zeitpunkt der Zuweisung hatte.

i = 30;

System.Console.WriteLine(i.ToString());  // 30
System.Console.WriteLine(k.ToString());  // 10

Referenztypen verhalten sich jedoch anders. Zum Beispiel könnten Sie zwei Variablen wie folgt deklarieren:

Employee ee1 = new Employee();
Employee ee2 = ee1;

Da Klassen in C# Referenztypen sind, wird ee1 als Verweis auf Employee bezeichnet. Die erste der vorherigen zwei Zeilen erstellt im Arbeitsspeicher eine Instanz von Employee und legt ee1 fest, um darauf zu verweisen. Wenn Sie also ee2 gleich ee1 festlegen, ist darin eine Kopie des Verweises auf die Klasse im Arbeitsspeicher enthalten. Wenn Sie nun die Eigenschaften von ee2 ändern, werden diese von ee1 übernommen, da beide auf das gleiche Objekt im Arbeitsspeicher verweisen, wie das folgende Beispiel zeigt:

Speicherorte für Referenztypen

Boxing und Unboxing

Der Vorgang des Konvertierens von Werttypen in Referenztypen wird als Boxing bezeichnet. Der umgekehrte Prozess, also das Konvertieren von Referenztypen in Werttypen, wird als Unboxing bezeichnet. Dies wird durch folgenden Code veranschaulicht:

int i = 123;      // a value type
object o = i;     // boxing
int j = (int)o;  // unboxing

In Java müssen Sie solche Konvertierungen manuell durchführen. Primitive Datentypen können durch Erstellen solcher Objekte oder durch Boxing in Wrapperklassenobjekte konvertiert werden. Dementsprechend können die Werte primitiver Datentypen aus diesen Wrapperklassenobjekten durch Aufrufen einer entsprechenden Methode in solch einem Objekt oder durch Unboxing extrahiert werden. Weitere Informationen zu Boxing und Unboxing finden Sie unter Boxing und Unboxing (C#-Programmierhandbuch).

Siehe auch

Konzepte

C#-Programmierhandbuch

Referenz

Typen (C#-Programmierhandbuch)

Weitere Ressourcen

Visual C#

Die Programmiersprache C# für Java-Entwickler