Homepage-Webhilfe Event-Banner

C#-Crashkurs

C# (ausgesprochen als C Sharp) ist eine objekt- und ereignisorientierte Programmiersprache von Microsoft, die für die .NET-Strategie entwickelt wurde. C#-Anwendungen werden in einen Zwischencode (CIL, Common Intermediate Language) übersetzt, der dann zur Laufzeit in Maschinencode übersetzt bzw. ausgeführt wird. Dadurch ist die Programmiersprache plattformunabhängig. Es muss lediglich eine CLI-Implementierung für die jeweilige Plattform existieren. Die gängigste Implementierung ist das Microsoft .NET Framework für Windows.

Dieser Crashkurs beschreibt die Grundlagen der Programmiersprache C# (in der Microsoft-Welt oftmals auch als Visual C# bezeichnet). Dieser Crashkurs soll lediglich als Referenz bzw. zum Auffrischen der Kenntnisse dienen. Falls Sie an einem ausführlichen Tutorial zu C# interessiert sind, so können Sie unser Tutorial auf unserer Partnerwebsite besuchen.


Der Syntax von C# orientiert sich an anderen C-ähnlichen Programmiersprachen (wie z. B. C++ oder Java). In C# haben Sie Anweisungen, welche mit einem Semikolon abgeschlossen werden müssen. Des Weiteren können Anweisungen gruppiert werden. Hierfür dient ein sogenannter Anweisungsblock, welcher mit Hilfe von geschweiften Klammern notiert wird. Solche Anweisungsblöcke kommen z. B. bei Abfragen, Schleifen oder Funktionen zum Einsatz. An einigen Stellen (z. B. Abfragen oder Schleifen) können die geschweiften Klammern weggelassen werden, wenn innerhalb des Anweisungsblocks nur eine Anweisung notiert wird.


Eine Variable ist immer an einen Datentyp gebunden. Dieser muss bei der Deklaration angegeben werden. Zusätzlich muss ein Name angegeben werden, welcher innerhalb des Gültigkeitsbereichs eindeutig sein muss und sich nicht mit anderen Bestandteilen (z. B. Klassen- oder Schnittstellennamen) überschneiden darf.

int meineZahl;

Für eine bessere Lesbarkeit des Quellcodes empfiehlt sich die Angabe eines Datentyppräfixes im Variablennamen. Dies sieht z. B. so aus:

int iMeineZahl;

Möchten Sie einer Variablen einen Wert zuweisen, so benötigen Sie den Zuweisungsoperator =:

iMeineZahl = 123;

Die erste Zuweisung einer Variablen wird auch als Variableninitialisierung bezeichnet. Die Deklaration und Initialisierung kann bei Bedarf auch kombiniert werden und somit innerhalb einer einzelnen Anweisung durchgeführt werden:

int iMeineZahl = 123;

An Stelle zur expliziten Angabe eines Datentyps kann auch das Schlüsselwort var verwendet werden. Dabei steht der Datentyp zwar noch nicht bei der Deklaration fest, jedoch wird die Variable bei der Initialisierung an den Datentyp gebunden, d. h. wurde einmal ein Wert zugewiesen, so kann bei einer zweiten Zuweisung der Datentyp nicht mehr geändert werden. Folgender Code wäre möglich:

var iMeineZahl = 123;
iMeineZahl = 456;

Folgender Code hingegen würde einen Compilerfehler auslösen:

var iMeineZahl = 123;
iMeineZahl = 4.56;

Es ist also letztendlich Geschmackssache, welche Schreibweise man bevorzugt. Die Notation mit var ist kürzer, wohingegen bei der Notation mit dem Datentyp sofort erkennbar ist, welchen Datentyp die Variable hat.

C# kennt keine primitive Datentypen. In C# wird jedoch zwischen Werttypen und Verweistypen unterschieden. Von den Werttypen gibt es zwei unterschiedliche Arten: Strukturen und Enumerationen (Aufzählungen). Strukturen sind mit Klassen zu vergleichen. Der Unterschied liegt im Prinzip darin, dass eine Struktur ein Wertetyp ist und somit auch auf dem Stack-Speicher abgelegt wird. Alle Datentypen, welche Sie u. U. von anderen Programmiersprachen als primitive Datentypen kennen, sind in C# als Struktur definiert. Für eine kürzere Notation gibt es Aliase, welche auf die jeweilige Struktur zeigen. Im Regelfall wird nicht der Strukturname, sondern der Alias notiert. Die folgende Tabelle zeigt diese „vordefinierten“ Datentypen:

Datentyp Alias Speicherbedarf Wertebereich Suffix Beschreibung
Boolean bool 8 Bits true oder false - Wahrheitswert
Char char 16 Bits '\u0000' .. '\uFFFF' - Unicode-Zeichen
SByte sbyte 8 Bits -128 .. 127 - Ganzzahl
Byte byte 8 Bits 0 .. 255 - Ganzzahl
Int16 short 16 Bits -32.768 .. 32.767 - Ganzzahl
UInt16 ushort 16 Bits 0 .. 65.535 - Ganzzahl
Int32 int 32 Bits -2.147.483.648 .. 2.147.483.647 - Ganzzahl
UInt32 uint 32 Bits 0 .. 4.294.967.296 u Ganzzahl
Int64 long 64 Bits -9.223.372.036.854.775.808 .. 9.223.372.036.854.775.807 l Ganzzahl
UInt64 ulong 64 Bits 0 .. 18.446.744.073.709.551.616 ul Ganzzahl
Single float 32 Bits ±1,4E−45 .. ±3,4E+38 f Gleitkommazahl
Double double 64 Bits ±4,9E−324 .. ±1,7E+308 - Gleitkommazahl
Decimal decimal 128 Bits ±1,0E−28 .. ±7,9E+28 m Gleitkommadezimalzahl

Enumerationstypen werden immer dann verwendet, wenn bestimmte Zustände oder Werte in Form eines Namens abgelegt werden sollen. Eine Enumeration enthält eine Aufzählung mehrerer solcher Namen:

enum Monate
{
    Januar,
    Februar,
    Maerz,
    April,
    Mai,
    Juni,
    Juli,
    August,
    September,
    Oktober,
    November,
    Dezember
}

Der Zugriff auf einen Enumerationswert erfolgt mit Hilfe des Punktoperators:

Monate eAkteullerMonat = Monate.Februar;

Bei Bedarf können Sie den einzelnen Namen auch noch einen nummerischen Wert geben:

enum Farben
{
    Rot = 0xFF0000,
    Gruen = 0x00FF00,
    Blau = 0x0000FF
}

Kommen wir als nächstes zu den Verweistypen: Die Daten (dabei handelt es sich um ein Objekt) von Verweistypen werden auf dem Heap gespeichert. Eine Referenz zum Heap wird jedoch weiterhin auf dem Stack gespeichert. Sie können einer Variablen für einen Verweistyp über den Wert null mitteilen, dass es kein Objekt gibt (Referenz „ins Leere“). Alle Klassen leiten automatisch von der Klasse Object ab. Diese Klasse ist zudem über den Alias object erreichbar. Eine weitere Klasse, mit welcher Sie oft arbeiten werden, ist String. String ermöglicht es Ihnen, Zeichenketten zu speichern. Auch für die String-Klasse ist ein Alias verfügbar: string. Auf das Thema Zeichenketten und Objektorientierung gehen wir jedoch später noch genauer ein.


Ganzzahlen (auch Festpunktzahlen genannt) werden in C# i. d. R. in der Dezimalschreibweise notiert:

int iZahl = 123;

Optional ist es auch möglich, eine Zahl in hexadezimaler Schreibweise anzugeben. Dafür muss das Suffix 0x und die Zahl in hexadezimaler Schreibweise angegeben werden:

int iZahl = 0x7B;

Bei Gleitkommazahlen (oftmals auch als Gleitpunktzahlen bezeichnet) muss darauf geachtet werden, dass als Trennzeichen nicht das Komma, sondern der Punkt verwendet wird:

double dZahl = 123.45;

C# unterstützt die Verwendung von Operatoren für die mathematischen Grundrechenarten: + (Addition), - (Subtraktion), * (Multiplikation) und / (Division). C# beachtet die Punkt-Vor-Strich-Regelung und erlaubt die Verwendung von Klammern.

O = G + M;
V = (G * h) / 3;

Die Klasse Math verfügt über einige statische Funktionen, welche Ihnen beim Arbeiten mit Zahlen behilflich sein können. Die Funktionen Min() und Max() können dazu verwendet werden, den kleineren bzw. größeren Wert von zwei übergebenen Zahlen zu ermitteln:

int iMin = Math.Min(a, b);
int iMax = Math.Max(a, b);

Die Funktion Pow() ermöglicht es Ihnen, eine Potenzrechnung durchzuführen. Mit der Funktion Sqrt() können Sie die Quadratwurzel einer Zahl berechnen.

double dZahl = 9;
double dQuadrat = Math.Pow(dZahl, 2);
double dWurzel = Math.Sqrt(dZahl);

Um Zahlen zu runden, können Sie die Funktionen Floor() (Zahl abrunden), Ceiling() (Zahl aufrunden) und Round() (Zahl kaufmännisch runden) verwenden. Der Funktion Round() kann bei Bedarf ein Parameter übergeben werden, mit welchem Sie festlegen können, auf wie viele Nachkommastellen die Zahl gerundet werden soll.

double dZahl = 2.54;
double dKleinereZahl = Math.Floor(dZahl);
double dGroessereZahl = Math.Ceiling(dZahl);

Zeichenketten werden in C# durch den Datentyp string (Alias für die Klasse String) dargestellt. Zeichenketten werden in doppelten Anführungszeichen notiert. Ein einzelnes Zeichen (Datentyp char) hingegen wird in einfachen Anführungszeichen angegeben. string gehört jedoch, anders als die meisten anderen Datentypen (wie z. B. int, double und char), zu den Verweistypen, d. h. die Zuweisung des Werts null (keine Referenz auf ein Objekt) ist möglich und der eigentliche Wert wird auf dem Heap gespeichert.

string sName = "Max Mustermann";

Zeichenketten können über das Pluszeichen + kombiniert werden. Diesen Vorgang nennt man auch Verkettung.

string sVorname = "Max";
string sNachname = "Mustermann";
string sName = sVorname + " " + sNachname;

Die Klasse String enthält die Eigenschaft Length, welche die Länge der Zeichenkette enthält:

string sName = "Max Mustermann";
int iLaenge = sName.Length;

Auf ein Objekt des Datentyps string können Sie mit Hilfe des Indexoperators [] zugreifen, wie wenn es ein Array wäre. Dadurch haben Sie die Möglichkeit, einzelne Zeichen direkt auszulesen:

string sName = "Max Mustermann";
char cErstesZeichen = sName[0];
char cZweitesZeichen = sName[1];

Die Klasse String enthält zudem auch einige Methoden: StartsWith() (prüft ob die Zeichenkette mit dem angegebenen Zeichen oder der Zeichenkette beginnt), EndsWith() (prüft ob die Zeichenkette mit dem angegebenen Zeichen oder der Zeichenkette beginnt), IndexOf() (erstes Vorkommen des angegebenen Zeichens oder der Zeichenkette), LastIndexOf() (letztes Vorkommen des angegebenen Zeichens oder der Zeichenkette), ToLower() (wandelt die Zeichenkette in Kleinbuchstaben um), ToUpper() (wandelt die Zeichenkette in Großbuchstaben um), Replace() (entfernt die angegebene(n) Zeichen oder Zeichenkette durch eine andere), Substring() (extrahiert einen Teil einer Zeichenkette an Hand der Position und Länge) und Split() (teilt einer Zeichenkette an dem angegebenen Zeichen oder der Zeichenkette auf und gibt ein Array zurück). Alle genannten Funktionen, die die Zeichenkette verändern, verändern nicht die eigene Zeichenkette, sondern geben eine Kopie mit den durchgeführten Änderungen zurück. Hierzu ein paar Beispiele:

string sText = "Dies ist ein Text.";
string sKleinerText = sText.ToLower();
string sGrosserText = sText.ToUpper();
int iLeerzeichen = sText.IndexOf(' ');

Ein Array erlaubt es, mehrere Elemente eines beliebigen Datentyps in einer Variablen zu speichern. Um ein Array zu deklarieren, wird der Datentyp, gefolgt von einem eckigen Klammernpaar und einem ganz normalen Variablennamen, notiert. Zwischen den eckigen Klammern wird nichts angegeben, d. h. die Größe des Arrays ist zum Zeitpunkt der Deklaration unwichtig:

int[] aZahlenliste;

Anders ist dies bei der Zuweisung, denn hier muss die Größe angegeben werden. Da Arrays ebenfalls Objekte sind, werden diese (wie Objekte von anderen Klassen auch) mit dem Schlüsselwort new instanziiert (erstellt).

aZahlenliste = new int[10];

Natürlich kann die Deklaration und Initialisierung kombiniert werden:

int[] aZahlenliste = new int[10];

Möchten Sie auf ein Element (engl. item) eines Arrays zugreifen, so benötigen Sie den Indexoperator []. Innerhalb der Klammern geben Sie den Index an. Dabei handelt es sich i. d. R. um einen nummerischen Wert und beginnt bei 0, d. h. das erste Element hat den Index 0, das zweite den Index 1 usw..

int iErsteZahl = aZahlenliste[0];

Sie können mit diesem Syntax natürlich nicht nur ein Element auslesen, sondern auch ändern:

aZahlenliste[2] = 468;

Eine der wichtigsten Eigenschaft eines Array-Objekts ist Length, welche die Länge des Arrays wiederspiegelt:

int iLaenge = aZahlenliste.Length;

Übrigens: Die Klasse Array verfügt über einige statische Funktionen, die Ihnen beim Arbeiten mit Arrays hilfreich sein könnten. So erlauben Ihnen die Funktionen IndexOf() und LastIndexOf() das Suchen von Einträgen (ähnlich wie das Suchen von Zeichen in einer Zeichenkette). Die Funktion Reverse() kehrt die Reihenfolge aller Einträge in einem Array um. Das Sortieren eines Arrays können Sie mit der Funktion Sort() durchführen.

Als Liste bezeichnet man eine Art von Arrays, welche zur Laufzeit von der Größe verändert werden können. Die wichtigsten Klassen für Listen sind List<T> und ArrayList. List<T> ist stark typisiert bzw. generisch, d. h. der Datentyp steht, wie bei Arrays auch, bei der Deklaration fest. In ArrayList können Werte eines beliebigen Datentyps gespeichert werden. Dies kann jedoch zu Problemen führen, weshalb List<T> bevorzugt werden sollte.

ArrayList lDatenliste = new ArrayList;
List<int> lZahlenliste = new List<int>;

Beide Listtypen implementieren die IList-Schnittstelle, wodurch die meisten Eigenschaften und Funktionen für beide Typen verwendet werden können.

Um einer Liste einen Eintrag hinzuzufügen, wird die Add()-Methode verwendet. Der Eintrag wird dabei an das Ende angehängt. Möchten Sie das Element an einer bestimmten Stelle (nullbasierter Index) einfügen, so können Sie die Funktion Insert() verwenden.

lZahlenliste.Add(123);
lZahlenliste.Add(789);
lZahlenliste.Insert (1, 456);

Das Entfernen eines Elements ist mit Hilfe der Funktionen Remove() und RemoveAt() möglich. Remove() entfernt das erste Vorkommen des übergebenen Werts, wohingegen RemoveAt() das Element eines bestimmten Index entfernt.

lZahlenliste.Remove(789);
lZahlenliste.RemoveAt(1);

Möchten Sie den Listeninhalt löschen, so können Sie die Funktion Clear() aufrufen:

lZahlenliste.Clear();

Falls Sie ein Element suchen möchten, so kann Ihnen die Funktion Contains() und IndexOf() behilflich sein. Contains() prüft ob der angegebene Wert in der Liste vorkommt. IndexOf() prüft dies zwar ebenfalls, gibt jedoch keinen Wahrheitswert zurück, sondern den Index des ersten Vorkommens.

bool bEinsZweiDrei = lZahlenliste.Contains(123);
int iEinsZweiDrei = lZahlenliste.IndexOf(123);

Die Länge der Liste können Sie mit der Eigenschaft Count ermitteln:

int iLaenge = lZahlenliste.Count;

Möchten Sie auf die Elemente zugreifen, so können Sie auf die Liste mit Hilfe des Indexoperators [] zugreifen, wie wenn es ein Array wäre:

int iErsteZahl = lZahlenliste[0];
int iZweiteZahl = lZahlenliste[1];

Ein wichtiger Bestandteil von Programmiersprachen ist die Möglichkeit, Variablen oder Rückgabewerte von Funktionen auf einen bestimmten Zustand zu prüfen. C# bietet uns hier zwei Arten von Abfragen an: einfache Verzweigungen (mittels if-else) und mehrfache Verzweigungen (mittels switch-case).

Für eine einfache Verzweigung ist es notwendig, eine Bedingung aufzustellen. Solche Bedingungen werden auch an anderen Stellen (z. B. bei Schleifen) benötigt. Eine Bedingung muss in C# dabei immer einen boolschen Wert (Wahrheitswert) ergeben, d. h. true (wahr) oder false (unwahr). Wenn wir nun jedoch keine Variable vom Typ bool prüfen wollen, so müssen wir einen solchen Wert erzeugen. Dies ist mit Hilfe von folgenden Vergleichsoperatoren möglich:

== gleich
!= ungleich
< kleiner als
<= kleiner als oder gleich
> größer als
>= größer als oder gleich

Wichtig: Der Vergleich von zwei Verweistypen mit Hilfe der Operatoren == und != vergleicht im Regelfall nicht den Inhalt, sondern die Referenz (also die Adresse). Es ist jedoch möglich, Operatoren in Klassen zu überladen. Dies ist z. B. bei Zeichenketten (Klasse String) der Fall. Deshalb ergibt der Vergleich von zwei Zeichenketten mit dem ==-Operator, sofern beide Zeichenketten den gleichen Inhalt haben, true, obwohl sich die Adressen unterscheiden.

Hierzu folgendes Beispiel mit Verwendung einer if-Abfrage (einfache Verzweigung):

if (a > b)
{
    Console.WriteLine("Zahl a ist größer als b");
}

Mit Hilfe des sogenannten else-Zweigs ist es möglich, zusätzlich den Fall abzudecken, falls die Bedingung nicht zutrifft:

if (a > b)
{
    Console.WriteLine("Zahl a ist größer als b");
}
else
{
    Console.WriteLine("Zahl a ist nicht größer als b");
}

Solche einfache Verzweigungen können beliebig verschachtelt werden. Es ist aber auch möglich, zwischen if- und else-Block weitere else if-Blöcke zu notieren. Dies sieht dann z. B. so aus:

if (a == b)
{
    Console.WriteLine("Zahl a ist gleich groß wie Zahl b");
}
else if (a > b)
{
    Console.WriteLine("Zahl a ist größer als b");
}
else
{
    Console.WriteLine("Zahl a ist kleiner als b");
}

Bedingungen können über && (logisches Und) und || (logisches Oder) verknüpft werden. Wird && und || innerhalb einer Bedingung verwendet, so sollten (runde) Klammern zur Gruppierung genutzt werden. Möchten Sie den Wahrheitswert einer Bedingung umkehren (negieren), so können Sie das Ausrufezeichen ! verwenden.

if (a > b && a > c)
{
    Console.WriteLine("Zahl a ist größer als b und c");
}

Eine weitere Möglichkeit, um einen Vergleich bzw. eine Abfrage durchzuführen, ist die mehrfache Verzweigung. Dafür wird ein switch-Block notiert, in welchem der Wert / die Variable, welche mit anderen Werten verglichen werden soll, angegeben wird. Innerhalb des switch-Blocks werden nun ein oder mehrere case-Blöcke notiert. Dort wird dann jeweils der Vergleichswert angegeben. Es wird immer nur der Code des case-Blocks ausgeführt, bei welchem die „Bedingung“ zutrifft.

switch (iMultiplikator)
{
    case 1:
        Console.WriteLine(iValue + " V");
        break;
    case 1000:
        Console.WriteLine((iValue / 1000) + " KV");
        break;
    case 1000000:
        Console.WriteLine((iValue / 1000000) + " MV");
        break;
}

Möchten Sie für mehrere Fälle den gleichen Code ausführen, so ist es möglich, case-Blöcke zu stapeln:

case 0:
case 1:
    Console.WriteLine(iValue + " V");
    break;

Des Weiteren ist es möglich, einen default-Block anzugeben. Der Code des default-Blocks wird ausgeführt, wenn keine der definierten Fälle (case-Blöcke) zutreffen:

default:
    Console.WriteLine(iValue + " V");
    break;

Schleifen erlauben es, bestimmte Vorgänge mehrmals auszuführen. C# bietet uns hierfür 4 verschiedene Schleifen an. Die einfachste Schleife ist die while-Schleife. Die while-Schleife besteht aus einer Bedingung und einem Anweisungsblock. Der Anweisungsblock wird solange ausgeführt wie die Bedingung zutrifft. Die Bedingung wird dabei vor dem Ausführen der Anweisungen geprüft (kopfgesteuerte Schleife).

int i = 0;
while (i < 10)
{
    Console.WriteLine(i);
    i++;
}

Manchmal kann es jedoch auch hilfreich sein, eine Bedingung erst nach dem Ausführen des Codes zu prüfen. Dies hat dann auch zur Folge, dass der Code mindestens einmal ausgeführt wird. Eine solche fußgesteuerte Schleife lässt sich mit der do-while-Schleife realisieren. Im folgenden Beispiel besteht kein Unterschied zum obigen Beispiel.

int i = 0;
do
{
    Console.WriteLine(i);
    i++;
} while (i < 10);

Für die oben verwendeten Zählvorgänge wird i. d. R. eine for-Schleife verwendet. Die for-Schleife hat einen ausgeprägten Schleifenkopf. Dort kann eine Variablendeklaration und / oder -initialisierung, eine Bedingung und eine Anweisung, welche nach dem Ausführen des Schleifenrumpfs ausgeführt werden soll (meistens die Inkrementierung einer Variablen), angegeben werden. Die Bedingung wird, wie bei der while-Schleife auch, vor dem Ausführen des Anweisungsblocks überprüft.

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(i);
}

Für die Iteration über ein Array oder eine Liste kann die foreach-Schleife verwendet werden:

foreach (int iZahl : lZahlenliste)
{
    Console.WriteLine(iZahl);
}

Möchten Sie das Verhalten einer Schleife beeinflussen, so können Ihnen die Schlüsselwörter break und continue behilflich sein. break führt dazu, dass die Schleife beendet wird. Mit dem Schlüsselwort continue ist es möglich, die aktuelle Ausführung zu beenden und die Bedingung erneut zu prüfen. Das folgende Beispiel würde die Zahlen 0 bis 7, ausgenommen der Zahl 3, ausgeben:

for (int i = 0; i < 10; i++)
{
    if (i == 8)
        break;
    if (i == 3)
        continue;
    Console.WriteLine(i);
}

Schon des öfteren haben wir nun Funktionen genutzt, doch es ist auch möglich, eigene Funktionen zu definieren. Funktionen die nur durch eine Objektinstanz aufgerufen werden können, werden auch als Methoden bezeichnet.

Um eine Funktion zu definieren, benötigen wir einen Zugriffsmodifizierer (public, protected oder private), einen Rückgabetyp (Datentyp des Rückgabewerts), einen Namen und eine Parameterliste. Auf die Bedeutung der Zugriffsmodifizierer gehen wir nachher noch genauer ein. Als Rückgabetyp kann ein beliebiger Datentyp gewählt werden. Gibt die Funktion keinen Wert zurück, so muss als Rückgabetyp void angegeben werden. Der Name einer Funktion ist frei wählbar, muss jedoch eindeutig sein. Die Parameterliste wird in runden Klammern angegeben. Dabei besteht jede Parameterdeklaration aus Datentyp und Variablenname. Der Variablenname muss innerhalb der Funktion und deren Parameter eindeutig sein. Besitzt die Funktion keine Parameter, so wird eine leere Parameterliste notiert:

public void gebeTextAus()
{
    Console.WriteLine("Hallo Welt!");
}

Hier ein weiteres Beispiel mit Parametern:

public void addiereZahlen(int iZahlA, int iZahlB)
{
    Console.WriteLine("Ergebnis:" + (iZahlA + iZahlB));
}

Möchten Sie von einer Funktionen einen Wert zurückgeben, so können Sie eine return-Anweisung angeben. Die Verwendung von return ist auch bei Funktionen ohne Rückgabewert möglich, da das return-Statement die Ausführung der Funktion beendet.

public int addiereZahlen(int iZahlA, int iZahlB)
{
    return iZahlA + iZahlB;
}

In C# ist es auch möglich, Funktionen zu überladen. Beim Überladen von Funktionen gibt es mehrere Funktionen mit dem gleichen Namen, die sich jedoch von der Anzahl, Reihenfolge oder den Datentypen von Parametern unterscheiden.

public int addiereZahlen(int iZahlA, int iZahlB)
{
    return iZahlA + iZahlB;
}
public int addiereZahlen(int iZahlA, int iZahlB, int iZahlC)
{
    return iZahlA + iZahlB + iZahlC;
}

Die bisher hier vorgestellten Funktionen sind alles Methoden, d. h. der Aufruf dieser Funktionen ist nur dann möglich, wenn ein Objekt der Klasse instanziiert wurde. Möchten Sie eine Funktion, die auch ohne Objektinstanz aufgerufen werden kann, anlegen, so müssen Sie zwischen dem Zugriffsmodifizierer und dem Rückgabetyp das Schlüsselwort static angeben. Man spricht dann auch von einer statischen Funktion. Verwendet werden statische Funktionen oft für Hilfsfunktionen o. Ä..

public static int addiereZahlen(int iZahlA, int iZahlB)
{
    return iZahlA + iZahlB;
}

C# ist eine objektorientierte Programmiersprache, weshalb natürlich Klassen und Objekte zentraler Bestandteil der Sprache sind. Mit Hilfe von Klassen lassen sich komplexe Datentypen definieren, die mehrere Daten kapseln und / oder bestimmte Logikverhalten und Algorithmen implementieren können. Die Klasse stellt dabei eine Art Bauplan dar.

Im Regelfall wird jede Klasse in einer eigenen Datei abgelegt. Bei der Deklaration einer Klasse werden ein Zugriffsmodifizierer, das Schlüsselwort class und ein Klassenname angegeben:

public class Fernseher
{
    
}

Innerhalb der Klasse können Variablen, Eigenschaften (dazu gleich mehr) und Methoden notiert werden:

public class Fernseher
{
    private string sMarke;
    private int iBildschirmdiagonale;
    
    public void setMarke(string sMarke)
    {
        this.sMarke = sMarke;
    }
    
    public string getMarke()
    {
        return sMarke;
    }
    
    public void setBildschirmdiagonale(int iBildschirmdiagonale)
    {
        this.iBildschirmdiagonale = iBildschirmdiagonale;
    }
    
    public int getBildschirmdiagonale()
    {
        return iBildschirmdiagonale;
    }
}

Übrigens: Das this-Schlüsselwort verweist auf die aktuelle Objektinstanz und kann immer dann verwendet werden, wenn es zu Namenskonflikten kommt. Dies ist im Beispiel bei den setX()-Methoden der Fall, da als Parametername der gleiche Name wie für die Klassenvariable (auch als Membervariable bezeichnet) verwendet wurde.

Möchten Sie nun von dieser Klasse ein Objekt erzeugen (man spricht auch von einer Objektinstanz), so benötigen Sie das new-Schlüsselwort:

Fernseher oMeinFernseher = new Fernseher();

Der Zugriff auf Variablen, Funktionen etc. erfolgt mittels des Punktoperators .:

oMeinFernseher.setMarke("Samsung");
oMeinFernseher.setBildschirmdiagonale(39);

Übrigens: Auch beim Zugriff auf statische Bestandteile wird der Punktoperator . verwendet. Da im Regelfall keine Objektinstanz vorliegt, muss hier der Klassenname, der Punktoperator und anschließend der Name der statischen Variablen, Funktion etc. notiert werden.

Wie bereits angesprochen, gibt es in C# drei unterschiedliche Zugriffsmodifizierer: public, protected und private. Der Zugriffsmodifizierer private beschränkt den Zugriff auf die eigene Klasse. Mit dem Zugriffsmodifizierer protected ist es möglich, dass auch andere Klassen, die von der betroffenen Klasse erben, zugreifen können. Der Zugriffsmodifizierer public erlaubt zusätzlich den „Zugriff von außen“, d. h. ein Zugriff ist außerhalb der betroffenen Klassen möglich. Dieser Grund sorgt auch dafür, dass im obigen Beispiel die Methoden setMarke() und setBildschirmdiagonale() aufgerufen werden können. Würden Sie auf die Variablen sMarke und iBildschirmdiagonale zugreifen wollen, so würde Ihnen der Zugriff verwehrt werden. Es ist auch möglich, Zugriffsmodifizierer wegzulassen. In diesem Fall wird private angenommen.

Variablen sollten grundsätzlich den Zugriffsmodifizierer private oder protected haben. Deshalb müssen für Variablen im Regelfall getX() und setX()-Methoden programmiert werden. In C# wird diese Technik kaum angewendet, da hierfür die sogenannten Eigenschaften vorgesehen sind. Dies sieht z. B. so aus:

public int Bildschirmdiagonale { get; set; }

Ein Vorteil von Eigenschaften ist, dass Sie den Zugriff beschränken können. Im folgenden Beispiel wird eine Eigenschaft definiert, die zwar von überall gelesen werden kann, jedoch nur von der Klasse gesetzt werden darf:

public int Bildschirmdiagonale { get; private set; }

Des Weiteren ist es möglich, bei Eigenschaften einen Programmcode zu hinterlegen (z. B. um den Wert zu prüfen). In einem solchen Fall wird dann jedoch meistens zusätzlich eine Variable benötigt, die den Wert speichert:

private int iBildschirmdiagonale;

public int Bildschirmdiagonale
{
    get
    {
        return iBildschirmdiagonale;
    }
    set
    {
        if (value >= 20 && value <= 80)
        {
            iBildschirmdiagonale = value;
        }
    }
}

Wie Ihnen vermutlich aufgefallen ist, wird bei der Instanziierung eines Objekts der Klassenname, gefolgt von runden Klammern, angegeben. Dies kommt daher, da bei der Objektinstanziierung der sogenannte Konstruktor aufgerufen wird. Befindet sich in der Klasse kein Konstruktor so wird automatisch ein leerer Standardkonstruktor angelegt. Es ist jedoch möglich, eigene Konstruktoren anzulegen. Der Konstruktor hat keinen Rückgabetyp (auch nicht void) und besitzt immer den gleichen Namen wie die Klasse:

public Fernseher(string sMarke)
{
   this.sMarke = sMarke;
    this.iBildschirmdiagonale = 39;
}
public Fernseher(string sMarke, int iBildschirmdiagonale)
{
    this.sMarke = sMarke;
    this.iBildschirmdiagonale = iBildschirmdiagonale;
}

Übrigens: Neben dem Konstruktor gibt es auch einen Destruktor. Der Destruktor wird aufgerufen, wenn das Objekt zerstört wird. Die Objektzerstörung wird jedoch durch den Garbage Collector der CLI verwaltet, weshalb der exakte Zeitpunkt nicht direkt feststeht. Verwendet werden Destruktoren, um bestehende Ressourcen wieder freizugeben bzw. Verbindungen zu trennen.

Zuletzt wollen wir uns noch kurz mit dem Thema Vererbung auseinandersetzen. C# ermöglicht es, dass eine Klasse Variablen, Eigenschaften und Funktionen einer anderen Klasse erbt. Dafür werden bei der Klassendeklaration hinter dem Klassennamen zusätzlich ein Doppelpunkt und der Name der Elternklasse (oft auch als Basisklasse bezeichnet) angegeben.

public class SmartTV : Fernseher
{
    
}

Übrigens: Soll eine Klasse Schnittstellen implementieren, so können diese hier ebenfalls angegeben werden. Mehrere Namen werden durch Komma getrennt.

Wichtig: In C# werden Klassen, Strukturen und Schnittstellen in Namensräumen (namespace-Block) verwaltet. Möchten Sie einen Namensraum importieren, so benötigen Sie die using-Anweisung (z. B. using System.Collections.Generic um den Datentyp List<T> nutzen zu können).


Einige Funktionen des .NET Frameworks (oder auch Funktionen von Fremd-Libraries sowie eigene Funktionen) können Ausnahmefehler (engl. exceptions) auslösen. Ausnahmefehler können und sollten jedoch im Programmcode abgefangen werden. Dafür wird der Code, welcher einen Ausnahmefehler auslösen kann, innerhalb eines try-Blocks notiert. Anschließend muss mindestens ein catch-Block notiert werden. Der catch-Block kennzeichnet sich durch das Schlüsselwort catch und ein rundes Klammernpaar, in welchem ein Datentyp (Klasse Exception oder eine der vielen Kindklassen) und ein Variablenname angegeben wird. Werden innerhalb des try-Blocks unterschiedliche Ausnahmefehler ausgelöst (man spricht auch davon, dass Ausnahmefehler geworfen werden), so ist es u. U. nützlich, mehrere catch-Blöcke zu notieren. Möchten Sie jedoch alle Fehler gleich behandeln, so können Sie auch nur einen catch-Block notieren, bei welchem Sie Exception als Datentyp angeben.

List<int> lZahlenliste = new List<int>();
try
{
    // Folgender Aufruf löst einen Ausnahmefehler aus, da der Index größer als die Länge ist
    lZahlenliste.Insert(1, 123);
}
catch (ArgumentOutOfRangeException ex)
{
    // Ausgabe der Fehlermeldung
    Console.WriteLine(ex.ToString());
}

Übrigens: Wird ein Ausnahmefehler ausgelöst, so wird die Ausführung des try-Blocks sofort abgebrochen. Wenn Sie jedoch auf Dateien zugreifen oder Verbindungen geöffnet haben, sollten Sie diese zunächst schließen. Deshalb kann nach den try- und catch-Blöcken ein finally-Block notiert werden. Der dort notierte Code wird immer ausgeführt, egal ob der try-Block vollständig ausgeführt wurde oder auf Grund eines Fehlers abgebrochen wurde.

Übrigens: Möchten Sie Ausnahmefehler selber auslösen, so benötigen Sie das Schlüsselwort throw und ein Objekt von einer der Exception-Klassen. Natürlich können Sie auch eigene Exception-Klassen definieren. Diese müssen lediglich von einer der bestehenden Exception-Klassen ableiten.


C# ist eine ereignisorientierte Programmiersprache. Dadurch haben Sie die Möglichkeit, Ereignisse zu registrieren (oftmals auch als abonnieren bezeichnet) und dadurch auf bestimmte eingetretene Events zu reagieren (ohne einen Status dauerhaft prüfen zu müssen). Um ein Ereignis zu registrieren, benötigen Sie den +=-Operator. Je nach Event unterscheidet sich dabei auch die Ereignishandler-Klasse. Dem Konstruktor der Ereignishandler-Klasse wird die Referenz auf die Funktion (mit Hilfe des Funktionsnamens) übergeben.

Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKeyPressed); 

Sie können auch jederzeit ein bestehendes Ereignisabonnement kündigen:

Console.CancelKeyPress -= OnCancelKeyPressed;
Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu. Weitere Informationen OK