# Sprachelemente von C-Sharp

Dieser Artikel bietet eine Übersicht einiger Sprachelemente von C#.

## Bedingte Ausführung (if, else, switch)

Der if-Block hat zur Folge, dass Anweisungen zwischen geschweiften Klammern nur in Kraft treten, wenn die angegebene Bedingung erfüllt ist, also ihr Wert true ist.
Der else-Block kann nur in Verbindung mit einem if-Block stehen und wird nur ausgeführt, wenn die Anweisungen des if-Blocks nicht ausgeführt wurden.
Die switch-Anweisung stelle eine Fallunterscheidung dar, welche je nach Wert des Ausdrucks, andere Anweisungen ausgeführt.
Es wird immer bei dem case-Label fortgefahren, dessen Wert mit dem Ausdruck in der switch-Anweisung übereinstimmt. Wenn kein Fall zutrifft, wird zum optionalen default-Zweig gesprungen.
Im Gegensatz zu C und C++ hat die C#-switch-Anweisung keine fall-through-Semantik. Nicht-leere Zweige müssen mit einem break, continue, goto, throw oder return enden; leere Zweige können jedoch von weiteren case-Zweigen fortgesetzt werden, um mehrere Fälle mit einem Satz von Anweisungen zu behandeln:
In C# sind auch Strings als Prüfausdruck erlaubt:

## Schleifen (for, do, while, foreach)

Wird eine for-Schleife ausgeführt, wird zuerst der Startausdruck gültig. Ist dieser vollständig bearbeitet, werden die Anweisungen im Schleifenrumpf und anschließend der Inkrementierungsausdruck solange wiederholt abgearbeitet, bis die Gültigkeitsbedingung false ergibt, d. h., ungültig wird.
Die while-Schleife ist dagegen recht primitiv: sie wiederholt die Anweisungen, solange die Bedingung true zurückgibt. Die Bedingung der while-Schleife wird immer vor dem Anweisungsblock ausgewertet. Wenn die Bedingung von Anfang an nicht erfüllt ist, wird der Schleifenrumpf nicht durchlaufen.
Die Bedingung der Do-While-Schleife wird immer nach dem Anweisungsblock geprüft. Die Schleife wird daher mindestens ein Mal durchlaufen.
Mit der foreach-Schleife wird durch alle Mitglieder einer Sequenz iteriert. In der Schleife besteht nur lesender Zugriff auf die Schleifenvariable.

## Die Sprunganweisungen break, continue, goto, return, yield return/break

Die Anweisung continue veranlasst den nächsten Durchlauf einer Schleife (Dabei wird der restliche Code im Schleifenkörper nicht abgearbeitet). Im Beispiel wird der Text nur zweimal ausgegeben.
Die Anweisung break veranlasst das Programm, die nächste umschließende Schleife (oder das umschließende switch) zu verlassen. In diesem Beispiel werden nur die Zahlen 0, 1, 2, 3 und 4 ausgegeben.
Mit goto springt das Programm an das angegebene Sprungziel.
Die Benutzung von goto sollte jedoch möglichst vermieden werden, da dadurch der Quellcode in der Regel unleserlicher wird. Es gilt jedoch als akzeptiertes Sprachmittel, um tief verschachtelte Schleifen zu verlassen, da in diesen Fällen der Code mit goto lesbarer ist als durch mehrfache Verwendung von break oder anderen Sprachmitteln. Siehe auch Spaghetticode.
Innerhalb einer switch-Anweisung kann mittels goto case bzw. goto default zu einem der Fälle gesprungen werden.
Mit return wird die aktuelle Methode verlassen und der im Kopf der Methode vereinbarte Referenz- bzw. Wertetyp als Rückgabewert zurückgeliefert. Methoden ohne Rückgabewert werden mit dem Schlüsselwort void gekennzeichnet.
Eine Besonderheit bildet der Ausdruck yield return. Zweck ist es, in verkürzter Schreibweise eine Rückgabesequenz für eine Methode oder eine Eigenschaft zu erzeugen. Der Compiler nutzt hierzu einen eigenen Typ, der von System.Collections.Generic.IEnumerable<T> abgeleitet ist und somit mit einem foreach-Block durchlaufen werden kann. Jeder Aufruf von yield return fügt bis zum Verlassen der Methode/Eigenschaft ein neues Element der Sequenz hinzu. Wird das Konstrukt nicht einmal aufgerufen, ist die Sequenz leer:
Hierdurch muss keine temporäre Liste erzeugt werden, die die umgewandelten Zahlen erst zwischenspeichert und dann zurückgibt:
Jedes Element, das zurückgegeben wird, muss implizit in den Typ der Rückgabesequenzelemente konvertierbar sein.
Zum Abbrechen des Vorgangs kann die Anweisung yield break verwendet werden:
Die Anweisungen return und yield return können nicht gemeinsam verwendet werden.
Bei der Verwendung muss beachtet werden, dass die zurückgegebene Sequenz die ihr zugrundeliegende Logik verzögert ausführt, was bedeutet, dass der erste Durchlauf ein und derselben Sequenz andere Werte liefern kann, als beim zweiten Mal:

## Die using-Anweisung

Die using-Anweisung definiert einen Geltungsbereich, an dessen Ende der Speicher von nicht mehr benötigten Objekten automatisch freigegeben wird. Bei begrenzten Ressourcen wie Dateihandler stellt die using-Anweisung sicher, dass diese immer ausnahmesicher bereinigt werden.
Hier wird ein Font-Objekt erzeugt, das am Ende des Blocks automatisch durch Aufruf seiner Dispose-Methode wieder freigegeben wird. Dies geschieht selbst dann, wenn in dem Block eine Ausnahme ausgelöst wird. Der Vorteil liegt in der vereinfachten Schreibweise, denn intern wird daraus folgendes Konstrukt, das ansonsten manuell so formuliert werden müsste:
Klassen müssen die System.IDisposable-Schnittstelle implementieren, damit die using-Anweisung auf diese Weise eingesetzt werden kann.

## Objekte und Klassen

Wenn man von einem Objekt spricht, handelt es sich dabei in der Umgangssprache normalerweise um ein reales Objekt oder einen Gegenstand des täglichen Lebens. Beispielsweise kann das ein Tier, ein Fahrzeug, ein Konto oder Ähnliches sein.
Übertragen auf die objektorientierte Programmierung und C# ist ein Objekt ein Exemplar (siehe Schlüsselwort new) einer Klasse. Eine Klasse kann man dabei als Bauplan oder Baustelle von Objekten ansehen.
Jedes Objekt kann mittels der in ihm enthaltenen Felder (Variablen) verschiedene Zustände annehmen. Natürlich kann jede Klasse auch Methoden (Funktionen, die Tätigkeiten bewirken) anbieten. Zugriffe auf die Variablen können mittels Eigenschaften, einer speziellen Art von Funktionen die nach außen hin wie Felder aussehen, auf bestimmte zulässige Änderungen beschränkt werden, wodurch auch sogenannte Ereignisse ausgelöst werden können, über deren Stattfinden sich andere Objekte informieren lassen können. Wie in der objektorientierten Programmierung üblich, können sich alle vier Arten von Klassenbestandteilen wahlweise sowohl auf die einzelnen Exemplare als auch auf die Klasse als solche beziehen.
* Klasse
* Feld(er)
* Eigenschaft(en)
* Methode(n)
* Ereignis(se)
Beispiel für den Bauplan eines Autos:

## Die Klasse namens „object“

Die Klasse object (ein Alias für System.Object) ist ein Referenztyp, von dem jede Klasse abgeleitet wird. So kann durch implizite Typumwandlung jeder Objekttyp in object umgewandelt werden.
.NET (und damit auch C#) unterscheidet zwischen Werttypen und Referenztypen. Werttypen sind jedoch auch (über den Zwischenschritt ValueType) von object abgeleitet. Deshalb kann (mittels eines boxing/unboxing genannten Verfahrens) auch eine Variable vom Typ object auf einen Werttyp, z. B. int verweisen.

## Strukturen (struct)

Strukturen sind bereits als Sprachmittel aus Sprachen wie C++ oder Delphi (Records) bekannt. Sie dienen primär zur Erstellung eigener komplexer Datenstrukturen oder eigener Datentypen.
So kann zum Beispiel eine Raumkoordinate, bestehend aus einer X-, einer Y- und einer Z-Position, durch eine Datenstruktur Koordinate abgebildet werden, die sich aus drei Gleitkommazahlen einfacher oder doppelter Genauigkeit zusammensetzt.
C# fasst eine über das Schlüsselwort struct definierte Struktur als einen Wertetyp auf. Strukturen in C# können außerdem Methoden, Eigenschaften, Konstruktoren und andere Elemente von Klassen aufweisen; sie können aber nicht beerbt werden.
Der Unterschied einer Strukturinstanz im Vergleich zu einem Objekt besteht in ihrer Repräsentation im Speicher. Da keine zusätzlichen Speicherreferenzen benötigt werden, wie es bei einem Objekt erforderlich ist (Referenztyp), können Strukturinstanzen wesentlich ressourcenschonender eingesetzt werden. So basieren beispielsweise alle primitiven Datentypen in C# auf Strukturen.

## Aufzählungen (Enumerationen)

Aufzählungen dienen zur automatischen Nummerierung der in der Aufzählung enthaltenen Elemente. Die Syntax für die Definition von Aufzählungen verwendet das Schlüsselwort enum (Abkürzung für Enumeration).
Der in C# verwendete Aufzählungstyp ähnelt dem in C, mit der Ausnahme, dass ein optionaler ganzzahliger Datentyp für die Nummerierung der Elemente angegeben werden kann. Ohne Angabe eines Datentyps wird int verwendet.
Die Elementnummerierung in C# beginnt bei 0. Es ist aber auch möglich, wie in C, jedem Element – oder nur dem ersten Element – einen eigenen Startwert zuzuweisen (wie im obigen Beispiel). Dabei können sich die Anfangswerte wiederholen. Die Zählung beginnt dann jeweils von neuem bei dem definierten Startwert und Element.
In C# ist es auch möglich, ein bestimmtes Element einer Enumeration über seine Ordinalzahl anzusprechen. Hierzu ist aber eine explizite Typumwandlung notwendig.

### Flags

Neben der beschriebenen Variante des enum-Aufzählungstyps existiert noch eine spezielle Variante von Enumeration. Flags definieren Enumerationen auf Bitebene und werden durch die Metainformation [Flags] vor der Enum-Deklaration definiert. Flag-Elemente können auf Bitebene verknüpft werden, sodass mehrere Elemente zu einem neuen kombiniert werden können. Hierzu müssen den zu kombinierenden Elementen Zweierpotenzen als Werte zugewiesen werden, damit eine Kombination ermöglicht wird.

## Zugriffsmodifikatoren

Zugriffsmodifikatoren regeln den Zugriff auf Klassen und deren Mitglieder (Methoden, Eigenschaften, Variablen, Felder und Ereignisse) in C#. Die folgende Tabelle führt die von C# unterstützten Zugriffsmodifikatoren auf und beschreibt deren Wirkung und den Sichtbarkeitskontext.
| Name | Wirkung |
| abstract | Abstrakte Member sind zwar deklariert, aber nicht implementiert. Die Implementierung erfolgt zwingend in der abgeleiteten Klasse. Von einer abstrakten Klasse kann keine Instanz erzeugt werden. |
| internal | internal beschränkt den Zugriff auf Klassen und deren Mitglieder auf eine Assembly. Es handelt sich hierbei nicht um eine Beschränkung auf Namespaces, sondern auf die Assembly (also auf die jeweilige ausführbare Datei oder die Klassenbibliothek). Unter Java entspricht das der Beschränkung auf ein package. |
| new | Hier ist nicht der Aufruf des Konstruktors gemeint. Mit dem new-Modifizierer wird ein Member der Basisklasse verdeckt. Der Klassen-Member besitzt dieselbe Signatur, aber eine andere Funktionalität und steht mit dem verdeckten Member der Basisklasse nicht in Beziehung. Beim Aufruf sind alle Basisklassenmember mit demselben Namen ausgeblendet. Ohne diesen Modifizierer gibt der Compiler eine Warnung aus. |
| override | Der Modifizierer override überschreibt die abstrakte oder virtuelle Implementierung einer Methode oder Eigenschaft bzw. eines Indexers oder Ereignisses der Basisklasse. override-Member sind automatisch virtual. In der Vererbungshierarchie handelt es sich um eine weitere Implementierung. |
| private | Beschränkt den Zugriff auf eine Klasse und deren Mitglieder. Eine mit private deklarierte Klasse kann nur innerhalb der Klasse selbst instanziert werden (beispielsweise kann ein öffentlicher Konstruktor einer Klasse, oder die statische Funktion Main, einen privaten Konstruktor aufrufen, der nicht von außen aufgerufen werden kann). Oft wird private verwendet, um das Singleton-Muster umzusetzen (z. B. bei der Verwendung einer Klasse als Fabrik; siehe Fabrikmethode) oder die Vererbung zu beeinflussen oder zu verbieten (siehe auch Schlüsselwort sealed). Hinweis: Eine abgeleitete Klasse kann auf private Mitglieder der Basisklasse (vgl. Schlüsselwort base) ebenfalls nicht zugreifen. Soll ein solcher Zugriff möglich sein, so muss der Zugriffsmodifikator protected verwendet werden. |
| protected | Der Modifikator protected erlaubt den Zugriff nur in der eigenen Vererbungshierarchie, also nur für die deklarierende Klasse und alle abgeleiteten Klassen. |
| protected internal | Der Modifikator protected internal ist eine Kombination aus den Modifikatoren protected und internal. Die Methode ist sichtbar für die deklarierende und für alle abgeleiteten Klassen sowie für alle Klassen innerhalb der Assembly. Eine Sichtbarkeit nur für abgeleitete Klassen innerhalb der Assembly existiert in C# nicht. In diesem Fall muss auf den Modifikator internal zurückgegriffen werden. |
| public | Auf als public gekennzeichnete Klassen oder Klassenmitglieder (z. B. Methoden oder Eigenschaften) kann unbeschränkt zugegriffen werden. Sie werden deshalb auch als „öffentlich“ bezeichnet. |
| sealed | sealed kann auf Klassen, Instanzmethoden und Eigenschaften angewendet werden. Von versiegelten Klassen kann nicht abgeleitet werden. Eine versiegelte Methode überschreibt eine virtuelle Methode der Basisklasse. Die versiegelte Methode bleibt für erbende Klassen sichtbar, eine weitere Überschreibung durch sie ist jedoch nicht mehr möglich. Dies ist somit die letzte Implementierung in der Vererbungshierarchie. |
| static | static-Member sind klassenspezifisch, aber nicht instanzspezifisch. Von statischen Klassen kann keine Instanz erzeugt werden. Statische Member einer Klasse werden mit dem Namen der Klasse, nicht mit dem Namen der Objektinstanz aufgerufen (Beispiel: Math.Sqrt). Für jede Instanz einer Klasse wird eine separate Kopie aller Instanzenfelder erzeugt, bei statischen Feldern ist lediglich eine Kopie vorhanden. |
| virtual | Der virtual-Modifizierer gibt an, dass es sich um die erste Implementierung in der Vererbungshierarchie handelt. Virtuelle Member können in einer abgeleiteten Klasse mit dem Modifikator override überschrieben werden. Fehlt virtual bei einem Member, handelt es sich um die einzige Implementierung. |
Hinweise:
* Per Voreinstellung sind Klassenmitglieder (Methoden, Eigenschaften usw.), denen kein Zugriffsmodifikator zugewiesen wurde, automatisch als private deklariert. Klassen selbst dagegen besitzen automatisch den Modifikator internal und sind nur in der aktuellen Assembly sichtbar.
* Die Modifikatoren können bis auf protected und internal nicht miteinander kombiniert werden. protected internal spielt im Zusammenhang mit der Vererbung von Komponenten eine Rolle. Die Sichtbarkeit der Basisklasse wird von der abgeleiteten Klasse übernommen.
* Zur Implementierung eines Schnittstellenmembers muss ein Klassenmember entweder als public deklariert werden oder der Schnittstellenmember muss explizit implementiert werden.

## Datentypen, Operatoren, Eigenschaften und Konstanten

C# kennt zwei Arten von Datentypen: Wertetypen und Referenztypen. Referenztypen dürfen dabei nicht mit Zeigern gleichgesetzt werden, wie sie u. a. aus der Sprache C++ bekannt sind. Diese werden von C# auch unterstützt, aber nur im „unsicheren Modus“ (engl. unsafe mode).
Wertetypen enthalten die Daten direkt, wobei Referenztypen im Gegensatz dazu nur Verweise auf die eigentlichen Daten, oder besser, Objekte darstellen. Beim Lesen und Schreiben von Wertetypen werden die Daten dagegen über einen Automatismus, Autoboxing genannt, in einer Instanz der jeweiligen Hüllenklasse (engl. wrapper) gespeichert oder aus ihr geladen.
Die Zuweisung eines Wertes bzw. einer Referenz kann während der Deklaration erfolgen oder später, sofern nicht eine Konstante deklariert wurde. Die Deklaration erfolgt durch Angabe eines Datentyps gefolgt von einem Variablennamen:
Es können auch mehrere Variablen des gleichen Typs zeitgleich deklariert werden:
Ferner besteht die Möglichkeit, der Variablen bei der Deklaration auch gleich einen Wert oder eine Referenz zuzuweisen (Initialwert):
Auch die Mehrfachzuweisung eines Wertes an verschiedene Variablen ist möglich:
Einen Sonderfall der Zuweisung stellt die Deklaration von Feldern (Arrays) dar. Näheres hierzu im entsprechenden Abschnitt.

### Datentypen und Speicherbedarf

| Datentyp | Bit | Suffix | Vorz. | Alias für (struct type) |
| bool | 8 | | - | System.Boolean |
| byte | 8 | | N | System.Byte |
| char | 16 | | - | System.Char |
| decimal | 128 | m, M | - | System.Decimal |
| double | 64 | d, D | - | System.Double |
| float | 32 | f, F | - | System.Single |
| int | 32 | | J | System.Int32 |
| long | 64 | l, L | J | System.Int64 |
| sbyte | 8 | | J | System.SByte |
| short | 16 | | J | System.Int16 |
| uint | 32 | u, U | N | System.UInt32 |
| ulong | 64 | ul, UL | N | System.UInt64 |
| ushort | 16 | | N | System.UInt16 |
Datentypen sind in C# nicht elementar, sondern objektbasiert. Jeder der in der Tabelle aufgeführten Datentypen stellt einen Alias auf eine Klasse des Namensraumes System dar. Beispielsweise wird der Datentyp bool durch die Klasse System.Boolean abgebildet. Durch die Objektbasiertheit ist es möglich, Methoden auf Datentypen anzuwenden:
Vergleichbar mit C++, und anders als bei Java, gibt es unter C# vorzeichenbehaftete und vorzeichenlose Datentypen. Diese werden durch Voranstellen des Buchstabens s (für signed, englisch für vorzeichenbehaftet) und durch Voranstellen des Buchstabens u (für unsigned, englisch für vorzeichenlos) gekennzeichnet (sbyte, uint, ulong, ushort, mit Ausnahme von short). Die Gleitkomma-Datentypen (float, double, decimal) können neben einfacher auch doppelte Genauigkeit aufweisen und unterscheiden sich im Speicherbedarf. Dadurch ändert sich die Genauigkeit, was in der Anzahl der möglichen Nachkommastellen zum Ausdruck kommt.

### Konstanten (Schlüsselwort const)

Einem mit const deklarierten „Objekt“ kann nach der Deklaration und Initialisierung kein neuer Inhalt zugewiesen werden. Das „Objekt“ wird dadurch zu einer Konstanten.
Es muss dabei vom Compiler festgestellt werden können, dass der Wert, der einer Konstante zugewiesen wird, unveränderlich ist. Es ist also auch möglich, eine Konstante von einem Referenztypen zu definieren, allerdings darf dieser nur null zugewiesen werden.
Grund dafür ist, dass der Compiler alle Verwendungen von Konstanten bereits zum Zeitpunkt des Kompilierens ersetzt. Strukturen können nicht konstant sein, da sie in einem Konstruktor einen Zufallsgenerator benutzen könnten.
Fehlerhafte Zuweisungen einer Konstanten werden mit dem Kompilierfehler CS0133 vom C-Sharp-Kompilierer moniert.
Konstanten gibt es auch in anderen Sprachen (z. B. C++, Java). In Java werden Konstanten durch das Schlüsselwort final gekennzeichnet, in Fortran durch PARAMETER.

### Operatoren

Operatoren führen verschiedene Operationen an Werten durch und erzeugen dabei einen neuen Wert. Je nach Anzahl der Operanden wird zwischen unären, binären und ternären Operatoren unterschieden. Die Reihenfolge der Auswertung wird durch die Priorität und Assoziativität bestimmt und kann durch Klammerausdrücken geändert werden.
| Operatorrangfolge | Operator | Beschreibung |
| 1. Primäre Operatoren | x.y | Memberzugriff |
| 1. Primäre Operatoren | f(x) | Aufrufen von Methoden und Delegaten |
| 1. Primäre Operatoren | a[x] | Array- und Indexerzugriff |
| 1. Primäre Operatoren | x++ | Postinkrement |
| 1. Primäre Operatoren | x-- | Postdekrement |
| 1. Primäre Operatoren | new T(...) | Objekt- und Delegaterstellung |
| 1. Primäre Operatoren | new T(...){...} | Objekterstellung mit Initialisierern |
| 1. Primäre Operatoren | new {...} | Anonymer Objektinitialisierer |
| 1. Primäre Operatoren | new T[...] | Arrayerstellung |
| 1. Primäre Operatoren | typeof(T) | Abrufen des System.Type-Objekts für T |
| 1. Primäre Operatoren | checked(x) | Auswerten von Ausdrücken in überprüftem Kontext |
| 1. Primäre Operatoren | unchecked(x) | Auswerten von Ausdrücken in nicht überprüftem Kontext |
| 1. Primäre Operatoren | default (T) | Abrufen des Standardwerts vom Typ T |
| 1. Primäre Operatoren | delegate | Anonyme Methode |
| 2. Unäre Operatoren | +x | Identität |
| 2. Unäre Operatoren | -x | Negation |
| 2. Unäre Operatoren | !x | Logische Negation |
| 2. Unäre Operatoren | ~x | Bitweise Negation |
| 2. Unäre Operatoren | ++x | Präinkrement |
| 2. Unäre Operatoren | --x | Prädekrement |
| 2. Unäre Operatoren | (T)x | Explizites Konvertieren von x in den Typ T |
| 3. Multiplikative Operatoren | * | Multiplikation |
| 3. Multiplikative Operatoren | / | Division |
| 3. Multiplikative Operatoren | % | Modulo |
| 4. Additive Operatoren | x + y | Addition, Zeichenfolgenverkettung, Delegatkombination |
| 4. Additive Operatoren | x - y | Subtraktion, Delegatentfernung |
| 5. Schiebeoperatoren | x << y | Linksverschiebung |
| 5. Schiebeoperatoren | x >> y | Rechtsverschiebung |
| 6. Relationale Operatoren und Typoperatoren | x < y | Kleiner als |
| 6. Relationale Operatoren und Typoperatoren | x > y | Größer als |
| 6. Relationale Operatoren und Typoperatoren | x <= y | Kleiner oder gleich |
| 6. Relationale Operatoren und Typoperatoren | x >= y | Größer oder gleich |
| 6. Relationale Operatoren und Typoperatoren | x is T | Gibt true zurück, wenn x vom Typ T ist, andernfalls false. |
| 6. Relationale Operatoren und Typoperatoren | x as T | Gibt x als T typisiert zurück, oder NULL, wenn x nicht vom Typ T ist. |
| 7. Gleichheitsoperatoren | x == y | Gleich |
| 7. Gleichheitsoperatoren | x != y | Ungleich |
| 8. Logische, bedingte und NULL-Operatoren | x & y | Ganzzahliges bitweises AND, boolesches logisches AND |
| 8. Logische, bedingte und NULL-Operatoren | x ^ y | Ganzzahliges bitweises XOR, boolesches logisches XOR |
| 8. Logische, bedingte und NULL-Operatoren | x | y | Ganzzahliges bitweises OR, boolesches logisches OR |
| 8. Logische, bedingte und NULL-Operatoren | x && y | Wertet y nur aus, wenn x den Wert true hat. |
| 8. Logische, bedingte und NULL-Operatoren | x || y | Wertet y nur aus, wenn x den Wert false hat. |
| 8. Logische, bedingte und NULL-Operatoren | x ?? y | Ergibt y, wenn x den Wert NULL hat, andernfalls x |
| 8. Logische, bedingte und NULL-Operatoren | x ? y : z | Wird zu y ausgewertet, wenn x den Wert true hat, und zu z, wenn x den Wert false hat. |
| 9. Zuweisungsoperatoren und anonyme Operatoren | = | Zuweisung |
| 9. Zuweisungsoperatoren und anonyme Operatoren | x op= y | Verbundzuweisung. Entspricht x = x op y, wobei op der Operator +, -, *, /, %, &, | << oder >> ist. |
| 9. Zuweisungsoperatoren und anonyme Operatoren | (T x) => y | Anonyme Methode (Lambda-Ausdruck) |

#### Operatoren überladen

C# bietet die Möglichkeit, Operatoren für benutzerdefinierte Datentypen zu implementieren. Als benutzerdefinierter Datentyp gilt eine selbst geschriebene Klasse oder Struktur. Die Implementierung geschieht mit öffentlichen statischen Methoden. Statt eines Namens tragen sie das Schlüsselwort operator gefolgt von dem Operator-Zeichen welches überladen werden soll. Eine implizite oder explizite Typkonvertierung geschieht mittels implicit operator oder explicit operator als Methoden-Namen. Der Compiler ersetzt je nach Typ der Operanden den Quelltext in einen Aufruf der entsprechenden Methode:
Einschränkungen:
* Mindestens ein Parameter der Methode für die Überladung muss den Typ besitzen, für den der Operator überladen wird.
* Vergleichsoperatoren können nur paarweise überladen werden.
* Es können keine neuen Symbole verwendet werden. Nur die Operator-Symbole sind erlaubt, die in der Sprache definiert sind. Siehe dazu: Tabelle Operatoren
* Die Operatoren =, ., ?:, ->, new, is, sizeof, typeof und => können nicht überladen werden.
* Die Priorität eines benutzerdefinierten Operators kann nicht geändert werden. Der Vorrang und die Orientierung basiert auf dem Symbol des Operators.
* Die Anzahl der Operanden ist ebenfalls an das Symbol des Operators gebunden.
* Operatoren, die nicht reserviert sind, können nicht implementiert werden.
Um den Operator zu simulieren, können Indexer verwendet werden. Die Operatoren && und || können nicht direkt überladen werden. Sie werden über die speziellen überladbaren Operatoren true und false ausgewertet.
Die Operation x && y wird als T.false(x) ? x : T.&(x, y) ausgewertet, wobei T.false(x) ein Aufruf von dem in T deklarierten Operator false ist und T.&(x, y) ein Aufruf des ausgewählten Operator &.
Die Operation x || y wird als T.true(x) ? x : T.|(x, y) ausgewertet, wobei T.true(x) ein Aufruf von dem in T deklarierten Operator true ist und T.|(x, y) ein Aufruf des ausgewählten Operator |.

### Eigenschaften (Schlüsselwörter get, set und value)

Eine Eigenschaft (property) ist eine Sicht auf eine öffentliche Variable einer Klasse. Die Variable selbst wird durch einen Zugriffsmodifikator wie private oder protected (bei Variablen, die in abgeleiteten Klassen überschrieben werden sollen) für den Zugriff von außen gesperrt und über eine Eigenschaft zugänglich gemacht. Über die Eigenschaft kann dann bestimmt werden, ob ein lesender oder schreibender Zugriff auf die referenzierte Variable erfolgen darf. Beide Möglichkeiten sind auch miteinander kombinierbar.
Eine Eigenschaft wird durch Zuweisung eines Datentyps (der dem Datentyp der Variable entsprechen muss) zu einem Eigenschaftsnamen angelegt und hat eine ähnliche Struktur wie die Syntax einer Methode. Die Eigenschaft ist dabei wie eine Variable ansprechbar und ihr kann auch ein Zugriffsmodifikator zugewiesen werden. Eine Eigenschaft enthält selbst keine Daten, sondern bildet diese auf die referenzierte Variable ab (vergleichbar mit einem Zeiger).
Zur Abfrage einer Eigenschaft existiert in C# das Schlüsselwort get und zum Setzen eines Wertes das Schlüsselwort set. Von außen stellt sich die Eigenschaft dann wie eine Variable dar und der Zugriff kann entsprechend erfolgen (vgl. VisualBasic).
Die Programmiersprache Java verfolgt mit den Set- und Get-Methoden (Bean-Pattern, Introspection) das gleiche Ziel – alle Zugriffe erfolgen nie direkt über eine Variable, sondern über die entsprechende Methode.
Beispiel einer Eigenschaftsdefinition Wohnort für eine private Variable (_wohnort):
Durch das „Weglassen“ des Schlüsselwortes set oder des Schlüsselwortes get kann gesteuert werden, ob die Eigenschaft nur gelesen oder nur geschrieben werden darf. Das Schlüsselwort value ist dabei ein Platzhalter für den der Eigenschaft zugewiesenen Wert, der gesetzt werden soll. Es wird nur innerhalb eines set-Blocks als Schlüsselwort betrachtet (und entspricht in etwa einer temporären lokalen Variable).
Beispiel für den Zugriff auf die oben definierte Eigenschaft Wohnort:
Würde bei der obigen Definition der Eigenschaft ‚Wohnort‘ der get-Block weggelassen, so würde der lesende Zugriff zu einem Zugriffsfehler führen (im Beispiel in der Zeile, in der die Ausgabe erfolgt).
Neben dem einfachen Setzen oder Lesen einer Eigenschaft, können im set-Block bzw. get-Block auch Operationen ausgeführt werden, beispielsweise die Potenzierung eines bei set übergebenen Wertes (value mal Exponent), bevor er der Variablen zugewiesen wird. Das Gleiche gilt für das Schlüsselwort get. Theoretisch kann somit ein Zugriff für den Benutzer einer Klasse ganz unerwartete Ergebnisse bringen. Deshalb sollten alle Operationen, die Veränderungen auf einen Wert durchführen über normale Methoden abgebildet werden. Ausgenommen sind natürlich Wertprüfungen bei set.
Das Beispiel konkateniert den der Eigenschaft übergebenen Wert (hier: Musterstadt) zur Zeichenkette „12345“. Diese Aktion ist syntaktisch und semantisch richtig, sie sollte dennoch in einer Methode ausgeführt werden.
Ab C# 3.0 ist es möglich, Eigenschaften automatisch zu implementieren. Dies ist eine verkürzte Schreibweise für Eigenschaften, bei denen es sich um den Zugriff auf eine Variable handelt, die innerhalb der Klasse bzw. Struktur als Feld deklariert wurde.
Beispiel anhand der Struktur Point:
Das gleiche Beispiel mit Hilfe automatisch implementierter Eigenschaften:
Mit Hilfe des Objektinitialisierers (ab .NET 3.5) ist ein Konstruktor überflüssig:

### Indexer

Der Indexer ist die Standardeigenschaft von einem Objekt. Der Aufruf geschieht wie bei einem Array mit eckigen Klammern.
Beispiel: Zugriff auf die 32 Bits einer int Variable mit Hilfe eines Indexers.
Wie bei Eigenschaften kann der Zugriff auf nur lesend oder nur schreibend beschränkt werden, indem man den get-Accessor bzw. set-Accessor weglässt.
Unterschiede zu Arrays, Methoden und Eigenschaften:
* Indexer müssen mindestens ein Parameter besitzen.
* Im Gegensatz zu Arrays können beim Zugriff auch nicht ganzzahlige Werte verwendet werden.
public double this[string name] // ist erlaubt
* Indexer können überladen werden.
* Statische Indexer sind nicht erlaubt, sowie void als Rückgabewert.
* ref und out dürfen bei Indexern nicht verwendet werden.

### Darstellung spezieller Zeichen oder Zeichenfolgen („escapen“)

| Seq. | Beschreibung | Hexadezimal |
| \ | Einleitung alternativer Interpretation | 0x001B |
| \' | Einfaches Anführungszeichen | 0x0007 |
| \" | Doppeltes Anführungszeichen | 0x0022 |
| \\ | Umgekehrter Schrägstrich | 0x005C |
| \0 | Null | 0x0000 |
| \a | Signalton (engl. alert) | 0x0007 |
| \b | Rückschritt (engl. backspace) | 0x0008 |
| \f | Seitenvorschub (engl. form feed) | 0x000C |
| \n | Zeilenwechsel (engl. new line) | 0x000A |
| \r | Wagenrücklauf (engl. carriage return) | 0x000D |
| \t | Tabulatorzeichen, horizontal | 0x0009 |
| \v | Tabulatorzeichen, vertikal | 0x000B |
| \x | Hexadezimale Zeichenfolge für ein einzelnes Unicode-Zeichen | Hexadezimale Zeichenfolge für ein einzelnes Unicode-Zeichen |
| \u | Zeichenfolge für Unicode-Zeichen in Zeichenliteralen. | Zeichenfolge für Unicode-Zeichen in Zeichenliteralen. |
| \U | Zeichenfolge für Unicode-Zeichen in Zeichenketten-Literalen. | Zeichenfolge für Unicode-Zeichen in Zeichenketten-Literalen. |
Ein auf das Zeichen „\“ (umgekehrter Schrägstrich, engl. backslash) folgendes Zeichen wird anders interpretiert als sonst. Dabei handelt es sich meistens um nicht darstellbare Zeichen. Soll der umgekehrte Schrägstrich selbst dargestellt werden, so muss er doppelt angegeben werden („\\“).
Hexadezimale Zeichenfolge als Platzhalter für ein einzelnes Unicode-Zeichen: Das Zeichen wird dabei aus dem Steuerzeichen \x gefolgt von dem hexadezimalen Wert des Zeichens gebildet.
Zeichenfolge für Unicode-Zeichen in Zeichenliteralen: Das Zeichen wird dabei aus dem Steuerzeichen \u gefolgt von dem hexadezimalen Wert des Zeichens gebildet, z. B. „\u20ac“ für „€“ Der Wert muss zwischen U+0000 und U+FFFF liegen.
Zeichenfolge für Unicode-Zeichen in Zeichenkettenliteralen: Das Zeichen wird dabei aus dem Steuerzeichen \U gefolgt von dem hexadezimalen Wert des Zeichens gebildet. Der Wert muss zwischen U+10000 und U+10FFFF liegen. Hinweis: Unicode-Zeichen im Wertbereich zwischen U+10000 und U+10FFFF sind nur für Zeichenfolgen-Literale zulässig und werden als zwei Unicode-„Ersatzzeichen“ kodiert bzw. interpretiert (s. a. UTF-16).

## Vererbung
### Schnittstellen

Mehrfachvererbung wird in C# nur in Form von Schnittstellen (engl. interfaces) unterstützt.
Schnittstellen dienen in C# zur Definition von Methoden, ihrer Parameter, ihrer Rückgabewerte sowie von möglichen Ausnahmen.
An dieser Stelle ein Anwendungsbeispiel für die Mehrfachvererbung:
Schnittstellen in C# ähneln den Schnittstellen der Programmiersprache Java. Anders als in Java, dürfen Schnittstellen in C# keine Konstanten enthalten und auch keine Zugriffsmodifikator bei der Definition einer Methode vereinbaren.
Eine Klasse, die ein oder mehrere Schnittstellen einbindet, muss jede in der Schnittstelle definierte (virtuelle) Methode implementieren. Werden mehrere Schnittstellen eingebunden, die Methoden mit dem gleichen Namen und der gleichen Struktur besitzen (d. h. gleiche Parametertypen, Rückgabewerte usw.), so muss die jeweilige Methode in der implementierenden Klasse durch das Voranstellen des Namens der Schnittstelle gekennzeichnet werden. Dabei wird die jeweilige Funktion nur dann aufgerufen, wenn der Zeiger auf das Objekt vom entsprechenden Typ ist:

#### Ausgabe

Auch Schnittstellen ohne Methodendefinition sind möglich. Sie dienen dann als sogenannte Markierungsschnittstellen (engl. marker interface). Auf die Verwendung von marker interfaces sollte zu Gunsten von Attributen verzichtet werden. Schnittstellen können jedoch keine statischen Methoden definieren.
Das Einbinden einer Schnittstelle erfolgt analog zur Beerbung einer Klasse. Schnittstellen werden per Konvention mit einem führenden „I“ (für Interface) benannt.

#### Vererbung von Schnittstellen

Auswirkung der Zugriffsmodifikatoren bei Vererbung mit Interface :
| | kein Modifikator | new |
| Code | interface IMessage { string Message { get; } } public class MyClass : IMessage { public string Message { get { return "MyClass"; } } } public class MyDerivedClass : MyClass { public string Message { get { return "MyDerivedClass"; } } } | interface IMessage { string Message { get; } } public class MyClass : IMessage { public string Message { get { return "MyClass"; } } } public class MyDerivedClass : MyClass { public new string Message { get { return "MyDerivedClass"; } } } |
| Test (Verwendung) | MyDerivedClass mdc = new MyDerivedClass; MyClass mc = mdc as MyClass; IMessage m = mdc as IMessage; Assert.AreEqual(mdc.Message, "MyDerivedClass"); Assert.AreEqual(mc.Message, "MyClass"); Assert.AreEqual(m.Message, "MyClass"); | MyDerivedClass mdc = new MyDerivedClass; MyClass mc = mdc as MyClass; IMessage m = mdc as IMessage; Assert.AreEqual(mdc.Message, "MyDerivedClass"); Assert.AreEqual(mc.Message, "MyClass"); Assert.AreEqual(m.Message, "MyDerivedClass"); |
| | virtual & override | abstract & override |
| Code | interface IMessage { string Message { get; } } public class MyClass : IMessage { public virtual string Message { get { return "MyClass"; } } } public class MyDerivedClass : MyClass { public override string Message { get { return "MyDerivedClass"; } } } | interface IMessage { string Message { get; } } public class MyClass : IMessage { public abstract string Message { get; } } public class MyDerivedClass : MyClass { public override string Message { get { return "MyDerivedClass"; } } } |
| Test (Verwendung) | MyDerivedClass mdc = new MyDerivedClass; MyClass mc = mdc as MyClass; IMessage m = mdc as IMessage; Assert.AreEqual(mdc.Message, "MyDerivedClass"); Assert.AreEqual(mc.Message, "MyDerivedClass"); Assert.AreEqual(m.Message, "MyDerivedClass"); | MyDerivedClass mdc = new MyDerivedClass; MyClass mc = mdc as MyClass; IMessage m = mdc as IMessage; Assert.AreEqual(mdc.Message, "MyDerivedClass"); Assert.AreEqual(mc.Message, "MyDerivedClass"); Assert.AreEqual(m.Message, "MyDerivedClass"); |
Das Überschreiben einer Methode durch eine abgeleitete Klasse kann mit sealed verhindert werden:
Ein Interface kann auch durch eine Basisklasse implementiert werden:

### Das Schlüsselwort base

Das Schlüsselwort wird im Zusammenhang von Vererbung genutzt. Vereinfacht gesagt ist die Basisklasse, das was this für die aktuelle Klasse ist. Java hingegen sieht hierfür das Schlüsselwort super vor.
Nun folgt ein Beispiel, das die Verwendung von base zeigt:
In diesem Beispiel wurde die Verwendung nur anhand des Basisklassenkonstruktors gezeigt. Wie in der Einleitung beschrieben, kann base auch für den Zugriff auf die Mitglieder der Basisklasse benutzt werden. Die Verwendung erfolgt äquivalent zur Verwendung von this bei der aktuellen Klasse.

### Versiegelte Klassen

Versiegelte Klassen sind Klassen, von denen keine Ableitung möglich ist und die folglich nicht als Basisklassen benutzt werden können. Bekanntester Vertreter dieser Art von Klassen ist die Klasse String aus dem Namensraum System. Der Modifizierer sealed kennzeichnet Klassen als versiegelt. Es ist jedoch möglich versiegelte Klassen mit Erweiterungsmethoden zu erweitern.

### Statische Klassen

Analog zu Visual Basic .NET Modulen, können in C# Klassen definiert werden, die ausschließlich aus statischen Elementen bestehen:

### Erweiterungsmethoden

Ab der Version 3.0 können Datentypen erweitert werden. Hierzu wird eine statische Klasse definiert. Erweiterungsmethoden (engl. extensions) beinhalten jeweils einen ersten Parameter, der mit dem Schlüsselwort this beginnt, gefolgt von der gewöhnlichen Definition des Parameters:
Sofern die Klasse ExtensionKlasse für eine andere Klasse sichtbar ist, werden nun alle Zahlen vom Typ int mit der Methode MalDrei erweitert ohne aber den Typ int wirklich zu ändern. Der Compiler macht hierbei intern nichts anderes als die Methode MalDrei der Klasse ExtensionKlasse aufzurufen und den Wert 1993 als ersten Parameter zu übergeben.

## Methoden
### Anonyme Methoden

Anonyme Methoden werden u. a. verwendet, um Code für ein Event zu hinterlegen, ohne in einer Klasse eine Methode mit einem eindeutigen Namen definieren zu müssen. Anstelle des Methodennamens steht das Schlüsselwort delegate:

### Lambdaausdrücke

Ab der Version 3.0 besteht die Möglichkeit, anonyme Methoden in kürzerer Form zu definieren. Dies geschieht mit dem Operator Lambda => (ausgesprochen: „wechselt zu“). Auf der linken Seite des Lambda-Operators werden die Eingabeparameter angegeben, auf der rechten Seite befindet sich der Anweisungsblock bzw. ein Ausdruck. Handelt es sich um einen Anweisungsblock, spricht man von einem Anweisungslambda. Ein Ausdruckslambda, wie zum Beispiel x => x * x, ist hingegen ein Delegate, dessen einzige Anweisung ein return ist. Der Typ der Eingabeparameter kann weggelassen werden, wenn der Compiler diese ableiten kann. Lambda-Ausdrücke können sich auf äußere Variablen beziehen, die im Bereich der einschließenden Methode oder des Typs liegen, in dem der Lambda-Ausdruck definiert wurde.

## LINQ

LINQ definiert drei Dinge:
* Eine Syntax für Abfrage-Ausdrücke, die sich stark an SQL orientiert,
* Übersetzungsregeln,
* Namen (kaum mehr) für Methoden, die in Übersetzungsergebnissen benutzt werden.
Implementiert wird die Funktionalität durch sogenannte LINQ-Provider, die die namentlich definierten Methoden zur Verfügung stellen. Einer davon ist zum Beispiel LINQ-to-Objects.
In MySQL könnte der obere Ausdruck bspw. folgendermaßen aussehen:

## Typumwandlungen

In C# ist jeder Variablen ein Datentyp zugeordnet. Manchmal ist es nötig, Typen von Variablen ineinander umzuwandeln. Zu diesem Zweck gibt es Typumwandlungsoperationen. Dabei gibt es implizite und explizite Typumwandlungen.
Eine implizite Typumwandlung erscheint nicht im Quelltext. Sie wird vom Compiler automatisch in den erzeugten Maschinen-Code eingefügt. Voraussetzung dafür ist, dass zwischen Ursprungs- und Zieltyp eine implizierte Typumwandlungsoperation existiert.
Für explizite Typumwandlungen sind in C# zwei Konstrukte vorgesehen:
(Zieldatentyp) Variable_des_Ursprungsdatentyps
Variable_des_Ursprungsdatentyps as Zieldatentyp
Während erstere Umwandlung im Fall einer ungültigen Typumwandlung eine Ausnahme auslöst, ist Letztere nur möglich, wenn der Zieldatentyp ein Referenzdatentyp ist. Bei einer ungültigen Typumwandlung wird hier dem Ziel der Nullzeiger zugewiesen.
Die unchecked-Anweisung bzw. Anweisungsblock dient dazu den Überlauf einer Ganzzahl zu ignorieren. Mit checked hingegen wird bei einem Überlauf ein OverflowException ausgelöst.
Zu den Typumwandlungen gehört auch das sogenannte „Boxing“. Es bezeichnet die Umwandlung zwischen Wert- und Referenztypen. Der Zieltyp wird wie bei der expliziten Konvertierung in Klammern vor den umzuwandelnden Typ geschrieben. Erfolgt dies implizit, so spricht man von „Autoboxing“.

### Benutzerdefinierte Typumwandlungen

C# erlaubt die Definition von benutzerdefinierten Typumwandlungen. Diese können als explizit oder implizit markiert werden. Implizite benutzerdefinierte Typumwandlung kommen u. a. bei der Überladungsauflösung zum tragen, während explizite dann verwendet werden, wenn oben genannte explizite Typumwandlungssyntax benutzt wird.
Siehe auch: Operatoren überladen, Explizite Typumwandlung

## Parametrische Polymorphie (Generics)

Bei der Definition von Interfaces, Klassen, Strukturen, Delegate-Typen und Methoden können Typparameter angegeben und gegebenenfalls mit Einschränkungen versehen werden. Werden Typparameter angegeben, so erhält man generische Interfaces, Klassen, und so weiter. Bei der späteren Benutzung solcher Generics füllt man die Typparameter mit konkreten Typargumenten.

### Definitionsseitige Ko- und Kontravarianz

Ab C#4 ist es möglich, Typparametern von Interfaces und Delegate-Typen eine Varianzannotation mitzugeben. Diese beeinflusst die Subtyprelation. Angenommen, der generische Typ heiße K und habe einen einzigen Typparameter T. Möglich sind dann:
* Kovarianz: K<out T>; Wenn A Subtyp von B ist, dann ist K<A> Subtyp von K<B>,
* Kontravarianz: K<in T>; Wenn A Subtyp von B ist, dann ist K<B> Subtyp von K<A>,
* Invarianz: K<T>; Es gibt keine Untertypbeziehung zwischen Instanziierungen von K mit verschiedenen Typargumenten T.
(Jedem Typparameter kann unabhängig von anderen eine Varianzannotation mitgegeben werden.)
Neben der Fortsetzung von Untertypbeziehungen von Typargumenten auf Instanzen von Generics beeinflusst die Varianzannotation auch, an welchen Stellen ein Typparameter benutzt werden darf. So sind beispielsweise die Benutzung von kovarianten Typparametern als Typen von Methodenargumenten und die Benutzung von kontravarianten Typparametern als Rückgabetypen nicht erlaubt.

## Attribute (Metadaten)

Attribute geben die Möglichkeit Metadaten für Assemblies, Funktionen, Parameter, Klassen, Strukturen, Enumerationen oder Felder anzugeben. Diese können Informationen für den Compiler enthalten, für die Laufzeitumgebung oder über Reflexion während der Laufzeit ausgelesen werden. In C# werden diese mit eckigen Klammern über dem Ziel angegeben. Das STAThread-Attribut wird z. B. benötigt, wenn ein Programm COM Interoperabilität unterstützen soll – Die Fehlermeldung lautet sonst: „Es wurde eine steuernde ‚IUnknown‘ ungleich NULL angegeben, aber entweder war die angeforderte Schnittstelle nicht 'IUnknown', oder der Provider unterstützt keine COM Aggregation.“
Attribute selbst sind wiederum Klassen, die von der Klasse Attribute abgeleitet sind, und können beliebig selbst definiert werden.
Beispiel:
Verwendung:
Abfrage von Attributen:

## Ausnahmen/Exceptions

Schema:
Es ist nicht zwingend erforderlich, dass immer alle Blöcke (catch, finally) angegeben werden. Den Umständen entsprechend kann auf einen try-Block auch direkt ein finally-Block folgen, wenn beispielsweise keine Behandlung einer Ausnahme erwünscht ist. Zudem ist es nicht zwingend erforderlich, dass auf ein try-catch-Konstrukt ein finally-Block folgen muss. Es ist jedoch nicht möglich, nur einen try-Block zu definieren. Ein try-Block muss mindestens von einem weiteren Block gefolgt werden.
Zudem ist es möglich, mehrere catch-Blöcke zu definieren. Wird im try-Bereich eine Ausnahme ausgelöst, so werden alle vorhandenen catch-Blöcke der Reihe nach durchgegangen, um zu sehen, welcher Block sich um die Ausnahme kümmert. Somit ist es möglich, gezielt verschiedene Reaktionen auf Ausnahmen zu programmieren.
Wird eine Ausnahme von keinem catch-Block abgefangen, so wird diese an die nächsthöhere Ebene weitergegeben.
Beispiele vordefinierter Ausnahme-Klassen:
* Exception
* SystemException
* IndexOutOfRangeException
* NullReferenceException
* InvalidOperationException
* ArgumentException
* ArgumentNullException
* ArithmeticException
* ArithmeticOutOfRangeException
* OverflowException
* DllNotFoundException
Bei der Implementierung eigener, nicht-kritischer Ausnahmen, ist darauf zu achten, nicht von der Klasse Exception abzuleiten, sondern von ApplicationException.

### Throw

Mittels throw ist es möglich eine, in einem catch-Block, aufgefangene Ausnahme eine Ebene höher zu „werfen“ (weitergeben). Somit ist es möglich, dass auch nachfolgender Code von der aufgetretenen Ausnahme informiert wird und somit seinerseits Aktionen unternehmen kann, um auf die Ausnahme zu reagieren. Der Aufruf-Stack bleibt erhalten.
Durch throw kann auch eine neue Ausnahme ausgelöst werden, beispielsweise eine programmspezifische Ausnahme, die nicht durch bereits vorhandene C#-Ausnahmen abgedeckt wird.

## Unsicherer Code

Durch die Codeverifizierung und .NET-Zugriffsverifizierung werden bei C# Speicherzugriffsfehler verhindert. Bei Verwendung von Zeigern werden diese Sicherheitsmechanismen umgangen. Dies ist nur in der Betriebsart „Unsafe Code“ möglich.
Beispiel:
Zudem können Klassen und Methoden als unsafe deklariert werden.

## Kommentare und Dokumentation

In C# sind, wie in C, C++ und Java, sowohl Zeilen- als auch Blockkommentare möglich.
Zeilenkommentare beginnen dabei mit zwei aufeinanderfolgenden Schrägstrichen (//) und enden in der gleichen Zeile mit dem Zeilenumbruch. Alles, was nach den Schrägstrichen folgt, wird bis zum Zeilenende als Kommentar angesehen und vom Compiler übergangen.
Blockkommentare, die sich über mehrere Zeilen erstrecken können, beginnen mit der Zeichenkombination /* und enden mit */.
Sowohl Zeilen- als auch Blockkommentare können zu Beginn oder auch mitten in einer Zeile beginnen. Blockkommentare können in derselben Zeile enden und es kann ihnen Quelltext folgen, der vom Compiler ausgewertet wird. Alles was innerhalb des Blockkommentars steht, wird vom Compiler übergangen.
Hinweis: Es sind auch Kommentare innerhalb einer Anweisung bzw. Deklaration möglich, z. B. zur Kommentierung einzelner Methodenparameter. Diese Art von Kommentaren sollte aus Gründen der Lesbarkeit und Wartbarkeit vermieden werden.
Zur Dokumentation von Methoden stellt C# in Form von Metadaten (Attribute) einen Mechanismus bereit, der es ermöglicht, eine XML-basierte Dokumentation erzeugen zu lassen.
Zur Dokumentation von Typen (das heißt, Klassen und deren Elemente wie Attribute oder Methoden) steht eine spezielle Form von Zeilenkommentaren bereit. Hierzu beginnt der Zeilenkommentar mit einem weiteren, dritten Schrägstrich (///) und befindet sich direkt über dem zu dokumentierenden Typ (z. B. einer Methode). Es folgen nun XML-Tags, die jeweils eine bestimmte Funktion bei der Dokumentation übernehmen, beispielsweise eine Zusammenfassung durch einen Summary-Tag. Alternativ kann auch ein Blockkommentar verwendet werden, der mit /** statt /* eingeleitet wird.
Statt die Dokumentation explizit einzufügen, kann auch eine externe Ressource referenziert werden, die die Dokumentation enthält:
Dokumentation im XMLDoc-API-Dokumentationsformat wird vom Compiler als ein normaler Zeilenkommentar angesehen. Erst durch Angabe der Compiler-Option „/doc“ werden Kommentare, die mit drei Schrägstrichen beginnen, als XMLDoc erkannt und aus dem Quellcode in eine Datei extrahiert. Diese kann dann weiterverarbeitet werden, z. B. ähnlich wie bei Javadoc zur Erstellung einer HTML-Hilfe verwendet werden (NDoc). Wenn die Entwicklungsumgebung dazu in der Lage ist, kann sie die formalisierte Dokumentation dazu benutzen, um an Verwendungsstellen aufgerufener Funktionen deren Beschreibung als Tooltip anzuzeigen; ebenso bei der Eingabe von Funktionsparametern.

## Schlüsselwörter
### Reservierte Schlüsselwörter

Die folgenden Bezeichner sind reservierte Schlüsselwörter und dürfen nicht für eigene Bezeichner verwendet werden, sofern ihnen nicht ein @-Zeichen vorangestellt wird (zum Beispiel @else).
abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while

### Kontextschlüsselwörter

Die folgenden Bezeichner sind Kontextschlüsselwörter, das heißt innerhalb eines gültigen Kontextes – und nur dort – haben sie eine besondere Bedeutung. Sie stellen aber keine reservierten Wörter dar, das heißt außerhalb ihres Kontextes sind sie für eigene Bezeichner erlaubt.
add alias ascending descending equals from get global group into join let on orderby partial remove select set value var where yield