Typen (C#-Programmierhandbuch)

Aktualisiert: Juli 2008

Typen, Variablen und Werte

C# ist eine stark typisierte Sprache. Jede Variable und jede Konstante verfügt über einen Typ, genau wie jeder Ausdruck, der zu einem Wert ausgewertet wird. Jede Methodensignatur gibt für jeden Eingabeparameter und den Rückgabewert einen Typ an. In der .NET Framework-Klassenbibliothek sind integrierte numerische Typen sowie komplexere Typen definiert, die eine große Anzahl logischer Konstrukte darstellen, z. B. das Dateisystem, Netzwerkverbindungen, Auflistungen und Arrays von Objekten sowie Datumsangaben. In einem typischen C#-Programm werden Typen aus der Klassenbibliothek sowie benutzerdefinierte Typen verwendet, die die Konzepte für das Problemfeld des Programms modellieren.

Folgende Informationen können in einem Typ gespeichert sein:

  • Der Speicherplatz, den eine Variable des Typs erfordert.

  • Die maximalen und minimalen Werte, die diese darstellen kann.

  • Die enthaltenen Member (Methoden, Felder, Ereignisse usw.)

  • Der Basistyp, von dem geerbt wird.

  • Die Position, an der der Arbeitsspeicher für Variablen zur Laufzeit reserviert wird.

  • Die Arten von zulässigen Operationen.

Der Compiler verwendet Typinformationen, um sicherzustellen, dass alle im Code ausgeführten Operationen typsicher sind. Wenn Sie z. B. eine Variable vom Typ int deklarieren, können Sie mit dem Compiler die Variable für Additions- und Subtraktionsoperationen verwenden. Wenn Sie dieselben Operationen für eine Variable vom Typ bool ausführen möchten, generiert der Compiler einen Fehler, wie im folgenden Beispiel dargestellt:

int a = 5;             
int b = a + 2; //OK

bool test = true;
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;
Hinweis:

C- und C++-Entwickler sollten beachten, dass in C# bool nicht in int konvertiert werden kann.

Der Compiler bettet die Typinformationen als Metadaten in die ausführbare Datei ein. Die CLR verwendet diese Metadaten zur Laufzeit, um die Typsicherheit zu gewährleisten, wenn Speicherplatz zugewiesen und freigegeben wird.

Angeben von Typen in Variablendeklarationen

Wenn Sie eine Variable oder Konstante in einem Programm deklarieren, müssen Sie ihren Typ festlegen oder das var-Schlüsselwort verwenden, damit der Typ vom Compiler abgeleitet wird. Im folgenden Beispiel werden einige Variablendeklarationen dargestellt, die sowohl integrierte numerische Typen als auch komplexe benutzerdefinierte Typen verwenden:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
            where item <= limit
            select item;

Die Methodenparameter- und Rückgabewerttypen werden in der Methodensignatur angegeben. Die folgende Signatur zeigt eine Methode, für die ein int als Eingabeargument benötigt wird und die eine Zeichenfolge zurückgibt:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = { "Spencer", "Sally", "Doug" };

Nachdem eine Variable deklariert wurde, kann sie nicht erneut mit einem neuen Typ deklariert werden. Außerdem kann ihr kein Wert zugewiesen werden, der nicht mit dem deklarierten Typ kompatibel ist. Zum Beispiel können Sie nicht eine int deklarieren und dieser dann den booleschen Wert true zuweisen. Werte können jedoch in andere Typen konvertiert werden, z. B., wenn diese neuen Variablen zugewiesen oder als Methodenargumente übergeben werden. Eine Typkonvertierung, die keinen Datenverlust verursacht, wird automatisch vom Compiler ausgeführt. Eine Konvertierung, die möglicherweise Datenverlust verursacht, erfordert eine Umwandlung in den Quellcode.

Weitere Informationen hierzu finden Sie unter Umwandlung und Typkonvertierungen (C#-Programmierhandbuch).

Integrierte Typen

C# liefert einen Standardsatz mit integrierten numerischen Typen zur Darstellung von ganzen Zahlen, Gleitkommawerten, booleschen Ausdrücken, Textzeichen, Dezimalwerten und anderen Datentypen. Es gibt auch integrierte string-Typen und object-Typen. Diese können Sie in jedem C#-Programm verwenden. Weitere Informationen über die integrierten Typen finden Sie im Typen-Referenztabellen (C#-Referenz).

Benutzerdefinierte Typen

Sie verwenden die Konstrukte struct, class, interface und enum, um eigene benutzerdefinierte Typen zu erstellen. Die .NET Framework-Klassenbibliothek ist eine Auflistung benutzerdefinierter, von Microsoft bereitgestellter Typen, die Sie in Ihren eigenen Anwendungen verwenden können. Standardmäßig sind die am häufigsten verwendeten Typen in der Klassenbibliothek in jedem C#-Programm verfügbar. Andere stehen nur zur Verfügung, wenn Sie ausdrücklich einen Projektverweis auf die Assembly hinzufügen, in der diese definiert sind. Wenn der Compiler über einen Verweis auf die Assembly verfügt, können Sie Variablen (und Konstanten) des in dieser Assembly deklarierten Typs im Quellcode deklarieren. Weitere Informationen hierzu finden Sie unter .NET Framework-Klassenbibliothek.

Das allgemeine Typsystem

Mit zwei grundlegenden Punktem über das System der Typen in .NET Framework sollten Sie vertraut sein:

  • Es unterstützt das Prinzip der Vererbung. Typen können von anderen Typen abgeleitet werden, die als Basistypen bezeichnet werden. Der abgeleitete Typ erbt (mit einigen Beschränkungen) die Methoden, Eigenschaften und anderen Member des Basistyps. Der Basistyp kann wiederum von einem anderen Typ abgeleitet sein. In diesem Fall erbt der abgeleitete Typ die Member beider Basistypen in der Vererbungshierarchie. Alle Typen, einschließlich integrierter numerischer Typen, z. B. System.Int32 (C#-Schlüsselwort: int), werden letztendlich von einem einzelnen Basistyp abgeleitet, nämlich System.Object (C#-Schlüsselwort: object). Diese einheitliche Typhierarchie wird als Allgemeines Typsystem (CTS) bezeichnet. Weitere Informationen zur Vererbung in C# finden Sie unter Vererbung (C#-Programmierhandbuch).

  • Jeder Typ im CTS ist als Werttyp oder Referenztyp definiert. Dies betrifft auch alle benutzerdefinierten Typen in der .NET Framework-Klassenbibliothek und Ihre eigenen benutzerdefinierten Typen. Typen, die Sie mithilfe des struct-Schlüsselworts definieren, sind Werttypen. Alle integrierten numerischen Typen sind structs. Typen, die Sie mithilfe des class-Schlüsselworts definieren, sind Referenztypen. Für Referenztypen und Werttypen gelten unterschiedliche Kompilierzeitregeln und ein anderes Laufzeitverhalten.

In der folgenden Abbildung wird die Beziehung zwischen Werttypen und Referenztypen im CTS dargestellt.

Werttypen und Referenztypen im CTS

Wert- und Verweistypen

Hinweis:

Wie Sie sehen, sind die alle am häufigsten verwendeten Typen im System-Namespace organisiert. Jedoch ist es für den Namespace, in dem ein Typ enthalten ist, unerheblich, ob es sich um einen Werttyp oder einen Referenztyp handelt.

Werttypen

Werttypen werden von System.ValueType abgeleitet, was wiederum von System.Object abgeleitet wird. Typen, die von System.ValueType abgeleitet werden, weisen ein besonderes Verhalten in der CLR auf. Werttypvariablen enthalten die zugehörigen Werte direkt, d. h., der Speicher wird inline in dem Kontext reserviert, in dem die Variable deklariert ist. Für Werttypvariablen erfolgt keine getrennte Heapreservierung oder Garbage Collection.

Zwei Kategorien von Werttypen sind verfügbar: struct und enum.

Die integrierten numerischen Typen sind Strukturen und verfügen über Eigenschaften und Methoden, auf denen Sie zugreifen können:

// Static method on type Byte.
byte b = Byte.MaxValue();

Sie deklarieren diese jedoch und weisen ihnen Werte zu, als wären es einfache, nicht aggregierte Typen:

byte num = 0xA;
int i = 5;
char c = 'Z';

Werttypen sind versiegelt (sealed). Dies bedeutet z. B., dass Sie keinen Typ von System.Int32 ableiten können und dass eine Struktur nicht von einer benutzerdefinierten Klasse oder Struktur erben kann, weil eine Struktur nur von System.ValueType erben kann. Eine Struktur kann jedoch eine oder mehrere Schnittstellen implementieren. Sie können einen Strukturtyp in einen Schnittstellentyp umwandeln. Dadurch wird eine Boxing-Operation gestartet, mit der die Struktur von einem Referenztypobjekt im verwalteten Heap umschlossen wird. Boxing-Operationen werden auch ausgeführt, wenn Sie einen Werttyp an eine Methode übergeben, die System.Object als Eingabeparameter akzeptiert. Weitere Informationen finden Sie unter Boxing und Unboxing (C#-Programmierhandbuch).

Sie können das struct-Schlüsselwort verwenden, um eigene benutzerdefinierte Werttypen zu erstellen. In der Regel wird eine Struktur als Container für einen kleinen Satz verwandter Variablen verwendet, wie im folgenden Beispiel dargestellt:

public struct CoOrds
{
    public int x, y;

    public CoOrds(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

Weitere Informationen über Strukturen finden Sie unter Strukturen (C#-Programmierhandbuch). Weitere Informationen über Werttypen im .NET Framework finden Sie unter Werttypen im allgemeinen Typsystem.

Die andere Kategorie von Werttypen ist die enum. Eine Enumeration definiert einen Satz benannter ganzzahliger Konstanten. So enthält z. B. die System.IO.FileMode-Enumeration in der .NET Framework-Klassenbibliothek einen Satz benannter ganzzahliger Konstanten, die festlegen, wie eine Datei geöffnet werden soll. Die Definition erfolgt wie im folgenden Beispiel:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Die Create-Konstante besitzt den Wert 2. Der Name ist jedoch für Personen, die den Quellcode lesen, viel aussagekräftiger. Aus diesem Grund ist es besser, an Stelle von Konstantenliteralen Enumerationen zu verwenden.

Alle Enumerationen erben von System.Enum, was wiederum von System.ValueType erbt. Alle Regeln, die für Strukturen gelten, gelten auch für Enumerationen. Weitere Informationen über Enumerationen finden Sie unter Enumerationstypen (C#-Programmierhandbuch).

Referenztypen

Ein Typ, der als Klasse, Delegat, Array oder Schnittstelle definiert ist, ist ein Referenztyp. Wenn Sie eine Variable für einen Referenztyp deklarieren, enthält die Variable zunächst den Wert NULL, bis Sie explizit eine Instanz des Objekts mithilfe des Operators new erstellen oder ihr ein Objekt zuweisen, das anderer Stelle mithilfe des folgenden Operators erstellt wurde: new, as shown in the following example:.

MyClass mc = new MyClass();
MyClass mc2 = mc;

Eine Schnittstelle muss zusammen mit einem Klassenobjekt initialisiert werden, von dem sie implementiert wird. Wenn IMyInterface von MyClass implementiert wird, erstellen Sie eine Instanz von IMyInterface, wie im folgenden Beispiel gezeigt:

IMyInterface iface = new MyClass();

Beim Erstellen des Objekts wird der Speicher im verwalteten Heap zugewiesen. Die Variable enthält lediglich einen Verweis auf den Speicherort des Objekts. Für Typen im verwalteten Heap ist ein Overhead erforderlich, wenn sie zugewiesen werden und wenn sie von der automatischen Speicherverwaltungsfunktion der CLR freigegeben werden, was als Garbage Collection bezeichnet wird. Die Garbage Collection ist jedoch auch stark optimiert. In den meisten Szenarien führt sie nicht zu einem Leistungsproblem. Weitere Informationen über die Garbage Collection finden Sie unter Automatische Speicherverwaltung.

Alle Arrays sind Referenztypen, selbst wenn ihre Elemente Werttypen sind. Arrays werden implizit von der System.Array-Klasse abgeleitet. Sie deklarieren und verwenden Sie diese jedoch mit der vereinfachten, von C# bereitgestellten Syntax, wie im folgenden Beispiel dargestellt:

// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };

// Access an instance property of System.Array.
int len = nums.Length;

Referenztypen bieten volle Vererbungsunterstützung. Wenn Sie eine Klasse erstellen, können Sie von einer anderen Schnittstelle oder Klasse erben, die nicht als versiegelt definiert ist. Andere Klassen können von Ihrer Klasse erben und die virtuellen Methoden überschreiben. Weitere Informationen zum Erstellen eigener Klassen finden Sie unter Klassen und Strukturen (C#-Programmierhandbuch). Weitere Informationen zur Vererbung und zu virtuellen Methoden finden Sie unter Vererbung (C#-Programmierhandbuch).

Typen von Literalwerten

In C# erhalten Literalwerte einen Typ vom Compiler. Sie können festlegen, wie ein numerisches Literal eingegeben werden soll, indem Sie am Ende der Zahl einen Buchstaben anfügen. Um z. B. anzugeben, dass der Wert 4.56 als Gleitkommazahl behandelt werden soll, fügen Sie nach der Zahl 4.56f ein "f" oder "F" an: Wenn kein Buchstabe angefügt wird, leitet der Compiler einen Typ für das Literal ab. Weitere Informationen darüber, welche Typen mit Buchstabensuffixen angegeben werden können, finden Sie auf den Referenzseiten für einzelne Typen unter Werttypen (C#-Referenz).

Da Literale typisiert sind und alle Typen letztlich von System.Object abgeleitet werden, können Sie Code der folgenden Art erstellen und kompilieren:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Generische Typen

Ein Typ kann mit einem oder mehreren Typparametern deklariert werden, die als Platzhalter für den eigentlichen Typ verwendet werden (den konkreten Typ), der vom Clientcode beim Erstellen einer Instanz des Typs bereitgestellt wird. Solche Typen werden als generische Typen bezeichnet. Beispielsweise besitzt der .NET Framework-Typ System.Collections.Generic.List<T> einen Typparameter, dem üblicherweise der Name T gegeben wird. Beim Erstellen einer Instanz des Typs geben Sie die Objekte an, die die Liste enthalten soll, z. B. string:

List<string> strings = new List<string>();

Die Verwendung des Typparameters ermöglicht die Wiederverwendung der Klasse für beliebige Elementtypen, ohne die einzelnen Elemente in object konvertieren zu müssen. Generische Auflistungsklassen werden als stark typisierte Auflistungen bezeichnet, weil der Compiler den jeweiligen Typ der Elemente in der Auflistung kennt und zur Kompilierzeit einen Fehler auslösen kann, wenn Sie beispielsweise versuchen, dem strings-Objekt im vorherigen Beispiel eine ganze Zahl hinzuzufügen. Weitere Informationen finden Sie unter Generika (C#-Programmierhandbuch).

Implizite Typen, anonyme Typen und Typen, die NULL-Werte zulassen

Wie bereits zuvor erläutert, können Sie eine lokale Variable (jedoch keine Klassenmember) implizit eingeben, indem Sie das var-Schlüsselwort verwenden. Die Variable erhält weiterhin zur Kompilierzeit einen Typ, aber der Typ wird vom Compiler bereitgestellt. Weitere Informationen finden Sie unter Implizit typisierte lokale Variablen (C#-Programmierhandbuch).

In einigen Fällen ist es unpraktisch, einen benannten Typ für einzelne Sätze verwandter Werte zu erstellen, die nicht außerhalb von Methodengrenzen gespeichert oder übergeben werden sollen. Sie können für diesen Zweck anonyme Typen erstellen. Weitere Informationen hierzu finden Sie unter Anonyme Typen (C#-Programmierhandbuch).

Gewöhnliche Werttypen können den Wert null nicht aufweisen. Sie können jedoch auf NULL festlegbare Werttypen erstellen, indem Sie nach dem Typ ein ? anfügen. Zum Beispiel ist int? ein int-Typ, der auch den Wert NULL aufweisen kann. Im CTS sind Typen, die NULL-Werte zulassen, Instanzen vom generischen Strukturtyp System.Nullable<T>. Typen, die NULL-Werte zulassen, sind besonders hilfreich, wenn Sie Daten in und aus Datenbanken übergeben, in denen die numerischen Werte NULL sein können. Weitere Informationen hierzu finden Sie unter Typen, die NULL-Werte zulassen (C#-Programmierhandbuch).

Verwandte Abschnitte

Weitere Informationen finden Sie unter den folgenden Themen:

C#-Programmiersprachenspezifikation

Weitere Informationen über Typen finden Sie in den folgenden Abschnitten von C#-Programmiersprachenspezifikation:

  • 1.3 Typen und Variablen

  • 3.8 Namespace- und Typnamen

  • 4.1. Werttypen

  • 4.2 Referenztypen

  • 4.3 Boxing und Unboxing

Siehe auch

Konzepte

C#-Programmierhandbuch

Konvertierung von XML-Datentypen

Referenz

Vergleich der Datentypen in verschiedenen Sprachen

Tabelle ganzzahliger Typen (C#-Referenz)

Weitere Ressourcen

C#-Referenz

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

Juli 2008

Einleitende Informationen und Informationen zu Typdeklarationen, zum allgemeinen Typsystem, zu Wert- und Verweistypen, Literalen und generischen Typen hinzugefügt.

Informationsergänzung.