Sprachelemente von C-Sharp
C# ist eine objektorientierte Programmiersprache, die vom Softwarehersteller Microsoft im Rahmen seiner .NET-Initiative eingeführt wurde. Die Syntax von C# ist an die Programmiersprachen C++ und Java angelehnt. An der Entwicklung von C# war Anders Hejlsberg maßgeblich beteiligt, der schon für den Softwarehersteller Borland die Programmiersprache Delphi entwickelte, die auf Pascal basiert.
C# ist ursprünglich ein Symbol für den in der Musik durch ein Kreuz um einen Halbton erhöhten Ton c, das cis - im Englisch "C sharp" (scharfes C). Teilweise wird C# auch mit "see sharp" (scharf sehen) übersetzt oder im deutschen Sprachgebrauch von wenigen Entwicklern "cis" genannt.
Konzept
Neben Konzepten der Programmiersprache Java greift C# auch Konzepte aus C++ sowie der Microsoft-eigenen Sprache VisualBasic auf. Beide Programmiersprachen sind ebenfalls, neben weiteren, als eigenständige Portierungen im .NET-Framework verfügbar und nutzbar.
Als eine Sprache der .NET-Plattform wird C# mittels einer virtuellen Laufzeitumgebung (runtime environment) in Form einer virtuellen Maschine (VM) ausgeführt. Durch die Verwendung einer virtuellen Maschine ist es - theoretisch - möglich, ein Programm unabhängig von Betriebssystem und Prozessorarchitektur auszuführen. Ein Programm, das beispielsweise unter dem Betriebssystem Windows auf einer Intel-Architektur entwickelt wurde, kann somit auch auf einem MacOS X-System auf einem PowerPC-Prozessor ausgeführt werden. Diese Plattformunabhängigkeit ist bei .NET bisher kaum gegeben, da der Hersteller Microsoft die .NET-Plattform nur für seine eigene Produktlinie Windows anbietet. Eine Laufzeitumgebung für das Betriebssystem Linux (und Derivate) steht in Form des Mono-Projekts in Aussicht, das vom Hersteller Ximian begonnen wurde.
Was C# von Java und C++ abhebt, ist die Tatsache, dass sowohl Klassen als auch Datentypen, wie in Smalltalk, durch Objekte repräsentiert werden. Ein weiterer Unterschied ist, dass in C# mit Attributen gearbeitet werden kann. Attribute erlauben es, einem Objekt Funktionalität hinzuzufügen, ohne seine Schnittstelle zu verändern. Man spricht hierbei auch von Metadaten.
Einige der Elemente von C++, die im Allgemeinen als unsicher gelten, wie beispielsweise Zeigerarithmetik, wurden in C# nur für so genannten "unsafe code" (unmanaged code) erlaubt. Dieser benötigt eine veränderte Sicherheitseinstellung, um auf einem Zielsystem ausgeführt zu werden, und einen zusätzlichen Compiler-Schalter, um ihn zu übersetzen. Somit wird vermieden, dass sich versehentlich unsicherer Code in eine Anwendung einschleicht.
Hallo Welt!
Hier der Klassiker aller Programme in C#:
using System;
class HalloWelt { static void Main() { Console.WriteLine("Hallo Welt!"); } }
Sprachelemente
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
if, elseif, else (Bedingte Ausführung)
if (Bedingung) { Anweisungen; } [else if (Bedingung) { Anweisungen; }] [else { Anweisungen; }]
Schleifen
for (Startausdruck; Gültigkeitsbedingung; Inkrementierungsausdruck) { Anweisungen; }
Die Bedingung der while-Schleife wird immer vor dem Anweisungsblock ausgeführt. Die Schleife durchläuft daher mindestens 0 mal.
while (Bedingung) { Anweisungen; }
Die Bedingung der do..while-Schleife wird immer nach dem Anweisungsblock ausgeführt. Die Schleife durchläuft daher mindestens 1 mal.
do { Anweisungen; } while (Bedingung);
Sprungbefehle
for (int i = 1; i <= 10; i++) { if (i < 9) continue; Console.WriteLine("Continue wurde nicht ausgeführt."); }
Mit dem Befehl continue wird der nächste Durchlauf einer Schleife veranlasst. (Dabei wird der restliche Code in der Schleife nach dem continue Befehl übersprungen. Im Beispiel wird der Text nur 2 mal ausgegeben.
for (int i = 1; i <= 100; i++) { if (i == 5) break; Console.WriteLine(i); }
Der Befehl break veranlasst das Programm, die nächste umschließende Schleife (oder switch) zu verlassen. In diesem Beispiel werden nur die Zahlen 1, 2, 3 und 4 ausgegeben.
int a = 1;
Top: if (a == 5) a++; goto Top; Console.WriteLine("Ende des Programs")
Mit goto springt das Programm an die angegebene Markierung.
int result = ImQuadrat(2);
static int ImQuadrat(int x) { int a; a = x*x; return a; }
Mit return wird die aktuelle Funktion verlassen und die angegebene Variable zurück gegeben.
throw
switch, case (Aufzählung)
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.
Jedes Objekt kann durch verschiedene Attribute beschrieben werden und verschiedene Zustände annehmen und diese auch auslösen.
Übertragen auf die objektorierentierte Programmierung und C# ist ein Objekt die Instanz (siehe Schlüsselwort new
) einer Klasse. Eine Klasse kann man dabei als Bauplan oder Gerüst eines Objektes ansehen.
Eine Klasse besitzt Eigenschaften (Variablen) bzw. Attribute, Methoden (die Zustände/Tätigkeiten darstellen) und Ereignisse, die die folge von Zuständen sind bzw. diese auslösen.
Attribut(e) |
Methode(n) |
Ereignis(se) |
Die Klasse object
Die Klasse objekt ist ein Referenztyp, von dem jede Klasse automatisch abgeleitet wird. So kann durch explizite Typumwandlung jeder Typ in object
umgewandelt werden und umgekehtrt.
Da es in C# (noch) keine generischen Sprachmittel gibt (geplant für .NET 1.2), wird object meistens genutzt, um Listen mit Objekten zu füllen. Diese werden vor dem Einfügen in object
konvertiert, eingefügt und später beim Bearbeiten wieder wieder zurückverwandelt. So muss man nicht für jede Klasse eine eigene Liste deklarieren.
Konstruktor
Destruktor
Das Schlüsselwort static
Das Schlüsselwort new
Das Schlüsselwort this
Schnittstellen (interface)
Strukturen (struct)
Flags
Aufzählungstypen (Enumerationen)
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 |
---|---|
internal |
|
private |
Beschränkt den Zugriff auf eine Klasse und deren Mitglieder. Eine mit Oft wird |
protected |
Dieser Modifikator ist mit dem Modifikator |
public |
Auf als |
Hinweise:
- Per Voreinstellung sind Klassenmitglieder (Methoden, Eigenschaften usw.), denen kein Zugriffsmodifikator zugewiesen wurde, automatisch als
private
deklariert. Klassen selbst dagegen besitzen automatisch den Modifikatorinternal
und sind nur im aktuellen Namensraum (namespace) sichtbar.
Wurde kein Namensraum zugewiesen, so gilt der so genannte globale Deklarationsraum als Standardnamensraum.
- Die Modifikatoren können bis auf
protected
undinternal
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.
- Schnittstellen sind immer öffentlich definiert. Die Zuordnung eines Zugriffsmodifikators (mit Ausnahme von
public
) ist nicht zulässig.
Datentypen, Eigenschaften und Konstanten
C# kennt zwei Arten von Datentypen: Wertetypen und Referenztypen. Referenztypen dürfen dabei nicht mit Zeigern (engl. pointer) 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 Automatimus, 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 die Variable nicht als Konstante deklariert wurde. Die Deklaration erfolgt durch Angabe eines Datentyps gefolgt von einem Variablennamen:
// Datentyp Variable; int i; System.Collections.IList liste;
Es können auch mehrere Variablen des gleichen Typs zeitgleich deklariert werden:
// Datentyp Variable1, Variable2, ...; int i, j, k; System.Collections.IList liste1, liste2;
Ferner besteht die Möglichkeit, der Variablen bei der Deklaration auch gleich einen Wert oder eine Referenz zuzuweisen (Initialwert):
// Datentyp Variable=Wert/Referenz; int i = 5; int j = 2, k = 3; System.Collections.IList liste = new System.Collections.ArrayList();
Auch die Mehrfachzuweisung eines Wertes an verschiedene Variablen ist möglich:
int i, j, k; i = j = k = 123;
Einen Sonderfall der Zuweisung stellt die Deklaration von Feldern (Arrays) dar. Näheres hierzu im entsprechenden Abschnitt.
Datentypen und Speicherbedarf
Datentyp | Bit | Vorz. | Hüllenklasse (struct type) |
---|---|---|---|
bool |
1 | - | System.Boolean
|
byte |
8 | J | System.Byte
|
char |
16 | - | System.Char
|
decimal |
128 | - | System.Decimal
|
double |
64 | - | System.Double
|
float |
32 | - | System.Single
|
int |
32 | J | System.Int32
|
long |
64 | J | System.Int64
|
sbyte |
8 | J | System.SByte
|
short |
16 | J | System.Int16
|
uint |
32 | N | System.UInt32
|
ulong |
64 | 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. Man spricht dabei auch von einer Hüllenklasse.
Durch die Objektbasiertheit ist es möglich, Methoden auf Datentypen anzuwenden, die die entsprechende Hüllenklasse bereitstellt:
|
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 Fließkomma- bzw. Gleitkomma-Datentypen (float, double, decimal
) können neben einfacher auch doppelte Genauigkeit aufweisen und haben einen variierenden Speicherbedarf.
Dadurch ändert sich die Genauigkeit, was in der Anzahl der möglichen Nachkommastellen zum Ausdruck kommt.
Konstanten (Schlüsselwort const)
Durch Verwendung des Schlüsselwortes const
wird einer Variablen ein konstanter Wert zugewiesen.
Einer mit const
deklarierten Variable kann nach der Deklaration und Wertzuweisung kein neuer
Wert bzw. keine neue Referenz zugewiesen werden. Die Variable wird als unveränderbare Konstante behandelt.
Eine Konstante kann dabei sowohl ein Wertetyp sein (also ein einfacher Datentyp), als auch ein Referenztyp, der auf ein Objekt verweist. Das Schlüsselwort const
sichert dabei nur, dass die Referenz
nicht veränderbar ist und schützt nicht vor Änderungen am referenzierten Objekt. So kann beispielsweise einer Konstanten die Referenz auf eine Liste zugewiesen werden, Änderungen an der Liste selbst sind weiterhin möglich (z.B. das Hinzufügen oder das Löschen von Listenelementen).
using System; using System.Collections;
public class ConstBeispiel { public static void Main() { // Neue Konstante erstellen const IList liste = new ArrayList();
// Der Liste zwei Elemente hinzufügen liste.Add("Element 1: string"); liste.Add("Element 2: string");
// FEHLER: Versuch der Variablen liste // eine neue Referenz zuzuweisen liste = new ArrayList(); } }
Das Schlüsselwort const
existiert auch in anderen Sprachen (z.B. C++) oder es existieren vergleichbare Schlüsselwörter (z.B. final
in der Programmiersprache Java oder PARAMETER
in Fortran).
Eigenschaften (Schlüsselwörter get, set und value)
Eine Eigenschaft (property) ist eine Sicht auf eine öffentliche Variable einer Klasse. Da es in der objektorientierten Programmierung schlechter Stil ist, Variablen als öffentlich zu deklarieren, weil somit unkontrolliert Wertänderungen durch andere Objekte erfolgen können, bietet C# die aus VisualBasic bekannten Eigenschaften.
Die Variable selbst wird durch einen Zugriffsmodifizierer wie private
oder protected
(bei Variablen, die in abgeleiteten Klassen überschreiben 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. Natürlich sind auch beide Möglichkeiten 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 Zugriffsmodifizierer zugewiesen werden. Eine Eigenschaft enthält selbst keine Daten, sondern bildet diese auf die referenzierte Variable ab (vergleichbar mit einem Pointer).
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 (OOP, Sichtbarkeit).
Beispiel einer Eigenschaftsdefinition Wohnort
für eine private Variable (_wohnort
):
public class EigenschaftBeispiel { private string _wohnort;
public string Wohnort { get { return (_wohnort); } set { _wohnort = "12345 " + value; } } }
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 Eigenschaften zugewiesenen Wert, der gesetzt werden soll. Er kann nur in Verbindung mit dem Schlüsselwort set
im entsprechenden Block verwendet werden (und entspricht in etwa einer temporären lokalen Variable).
Beispiel für den Zugriff auf die oben definierte Eigenschaft Wohnort
:
EigenschaftBeispiel instanz = new EigenschaftBeispiel(); instanz.Wohnort = "Musterstadt"; System.Console.WriteLine(instanz.Wohnort); // Ausgabe: 12345 Musterstadt
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, beispielweise 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.
Die Klasse string
Escape-Sequenzen
Methoden
Parameter
Die Schlüsselwörter in, out, ref
Parameterlisten
Rückgabewert
Indexer und Felder (Arrays)
Ereignisse und Delegierte (delegate)
Operatoren
Sichtbarkeit
Namensräume
Schlüsselwort using
Vererbung
Abstrakte Klassen
Schnittstellen
Das Schlüsselwort override
Das Schlüsselwort base
Das Schlüsselwort wird im Zusammenhang von Vererbung genutzt. Es ist, vereinfacht gesagt, das für die Basisklasse, was this
für die aktuelle Klasse ist.
Nun folgt ein Beispiel, das die Verwendung von base
zeigt:
public class Example : Basisklasse { private int myMember; public Example() : base(3) { myMember = 2; } }
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.
Das Schlüsselwort sealed
Der Modifizierer sealed
wird für so genannte versiegelte Klassen benutzt. Das 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 Namespace System
.
Explizite Konvertierung (Cast)
Weil C# eine typisierte Sprache ist, muss jede Variable einen Datentyp haben, dessen Wert sie aufnehmen kann. Da es manchmal nötig ist, Variablen zu konvertieren (d.h. ihren Typ zu verändern) gibt es Typkonvertierungsoperationen, die auch als Casting bezeichnet werden.
Die Konvertierung wird in 2 Kategorien aufgeteilt:
- implizite Konvertierung
- explizite Konvertierung (Casting)
Von der impliziten Konvertierung bekommt man normalerweise nichts mit. Bei problemlos möglichen Konvertierungen (z.B. von int
nach long
) wird sie vom Compiler automatisch durchgeführt.
Wenn jedoch beispielsweise Genauigkeitsverluste auftreten können (z. B. bei double
nach int
), muss die Konvertierung explizit durchgeführt werden.
Beispiel für explizite Konvertierung (Cast)
short s = 0; int i; // Wertbereich von i größer als von s s = i; // Fehler! s = (short) i; // OK! Konvertierung nach short.
Explizite Konvertierungen werden immer mit folgendem Konstrukt ausgeführt: (Zieldatentyp) Quelldatentyp
(Leserichtung von Rechts nach Links).
Neben der expliziten Konvertierung gibt es noch das so genannte "Boxing". Es bezeichnet die Umwandlung zwischen Wert- und Referenztypen. Der Zieltyp wird wie bei der expliziten Konvertierung in Klammern vor den zu konvertierenden Typ geschrieben. Erfolgt dies automatisch, so spricht man von "Autoboxing".
Überladen im Vergleich mit Überschreiben
Mehrfachvererbung
Mehrfachvererbung wird in C# nur in Form von Schnittstellen (interface) unterstützt. Bei Klassen wird nur die Einfachvererbung unterstützt.
Schnittstellen in C# können mit den abstrakten Klassen von C++, die rein virtuelle Funktionen besitzen, verglichen werden.
An dieser Stelle ein Beispiel, wie die Mehrfachvererbung angewandt werden kann:
public class MyInt : IComparable, IDisposable { // Implementierung }
Ausnahmebehandlung (Exceptions)
Assemblies
Attribute (Metadaten)
DLLs
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 Buchstabenkombination /* und enden mit */.
Sowohl Zeilen- als auch Blockkommentare können zu Beginn oder auch mitten in einer Zeile beginnen. Blockkommentare können in der selben 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 eines Befehls, z.B. zur Kommentierung einzelner Methodenparameter möglich. 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.
Verfügbare Klassenbibliotheken
- Collection
- Threads
- Reflection
- CodeDOM
- ADO.NET
- ASP.NET
- Windows.Forms
Verfügbarkeit (IDEs)
Neben der von Microsoft favorisierten Entwicklungsplattform VisualStudio.NET aus dem eigenen Hause, gibt es inzwischen auch Entwicklungsumgebungen (IDEs) anderer Hersteller für C#:
- C#-Builder von Borland
- Open Source-IDE SharpDevelop ( http://www.icsharpcode.net/OpenSource/SD/Default.aspx ).
Weblinks
- [1] Tutorial zur Programmiersprache C#. Enthält eine strukturierte Referenz aller Befehle und Schlüsselwörter.
- [2] HTML-Version des Buches: "C#", von Eric Gunnerson.
- [3] PDF C# Coding Style Guide von Mike Krüger.
- [4] Weitere Links zu C#
- netug e.V. (Usergroup)
- ECMA Seite