|
|
C# ist ein Akronym 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" übersetzt oder im deutschen Sprachgebrauch von wenigen Entwicklern "cis" genannt.
Neben Konzepten der Programmiersprache Java, greift C# auch Konzepte aus C++ sowie der Microsoft-eigenen propietären Sprache VisualBasic auf. Beide Programmiersprachen sind ebenfalls, neben weiteren, als eigenständige Portierungen im .NET Framework verfügbar und nutzbar.
Als eine (favorisierte) 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 möglich, ein Programm unabhängig von der Plattform (Betriebssystem, 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 einer PowerPC (PPC)-Architektur ausgeführt werden.
Diese Plattformunabhängigkeit ist bei C# bisher aber 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 aber in Form des Mono-Projekts in Aussicht, das vom Hersteller Ximian begonnen wurde. Nur lizenzrechtliche Probleme könnten zum Scheitern dieser Portierung führen.
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 Zeiger-Arithmetik, 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.
Zu Beginn der Klassiker aller Programme in C#:
{| style="background:#EEEEEE;"
|
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
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.
{| border="1" bordercolor="black" cellspacing="0" cellpadding="4"
|+ Klasse
|----- align="center"
| Attribut(e)
|----- align="center"
| Methode(n)
|----- align="center"
| Ereignis(se)
|}
Zugriffsmodifikatoren regeln den Zugriff auf Klassen und deren Mitglieder (Methoden, Eigenschaften, Variablen, Felder und/oder Ereignisse) in C#. Die folgende Tabelle führt die von C# unterstützten Zugriffsmodifikatoren auf und beschreibt deren Wirkung und den Sichtbarkeitskontext.
{| style="background:#DDEEFF;border:1px solid black;"
|----- valign="top"
! Name
! Wirkung
|----- valign="top"
|
Hinweise:
C# kennt zwei Arten von Datentypen: Wertetypen (value types) und Referenztypen (reference types).
Referenztypen dürfen dabei nicht mit Zeigern (pointer) gleichgesetzt werden, wie sie u. a. aus der Sprache C/C++ bekannt sind.
Diese werden von C# zwar auch unterstützt, aber nur im unsicheren Modus (unsafe mode).
Wertetypen enthalten die Daten direkt, wobei Referenztypen im Gegensatz dazu nur Verweise auf die eigentlichen Daten, oder besser,
Objekte darstellen. Bei Lesen und Schreiben von Wertetypen werden die Daten dagegen über einen Automatimus, Boxing genannt,
in einer Instanz der jeweiligen Wrapper-Klasse gespeichert oder aus ihr geladen.
Die Zuweisung eines Wertes bzw. einer Referenz kann während der Deklaration erfolgen oder später, sofern die Variable nichts als Konstante deklariert wurde. Die Deklaration erfolgt durch Angabe eines Datentyps gefolgt von einem Variablennamen:
{| style="background:#EEEEEE;"
|
Es können auch mehrere Variablen des gleichen Typs zeitgleich deklariert werden:
{| style="background:#EEEEEE;"
|
Ferner besteht die Möglichkeit, der Variablen bei der Deklaration auch gleich einen Wert oder eine Referenz zuzuweisen (Initialwert):
{| style="background:#EEEEEE;"
|
Auch die Mehrfachzuweisung eines Wertes an verschiedene Variablen ist möglich:
{| style="background:#EEEEEE;"
|
Einen Sonderfall der Zuweisung stellt die Deklaration von Feldern (Arrays) dar. Näheres hierzu im entsprechenden Abschnitt.
Eine Konstante kann dabei sowohl ein Wertetyp sein (also ein einfacher Datentyp), als auch ein Referenztyp,
der auf ein Objekt verweist. Das Schlüsselwort
Das Schlüsselwort
Eigenschaften (properties) sind Sichten auf öffentliche Variablen einer Klasse. Da es in der objektorientierten Programmierung schlechter Stil ist, Variablen
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 aber 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
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
Durch das "Weglassen" des Schlüsselwortes
Beispiel für den Zugriff auf die oben definierte Eigenschaft
Würde bei der oben gemachten Definition der Eigenschaft 'Wohnort' der
Neben dem einfachen Setzen oder Lesen einer Eigenschaft, können im
Das Beispiel konkateniert den der Eigenschaft übergebenen Wert (hier: Musterstadt) zur Zeichenkette "12345 ". Diese Aktion ist syntaktisch und semantisch richtig, aber sollte dennoch in einer Methode ausgeführt werden.
In C# sind, wie in C, C++ und Java, sowohl Zeilen- als auch Blockkommentare zulässig.
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, aber auch mitten in einer Zeile beginnen. Blockkommentare können in der selben Zeile enden und es kann ihnen Sourcecode folgen, der vom Compiler ausgewertet wird. Alles was innerhalb des Blockkommentars steht wird vom Compiler übergangen.
{| border="0" style="background:#eeeeee;"
|
Hinweis: Es sind auch Kommentare innerhalb eines Befehls, z. B. zur Kommentierung einzelner Methodenparameter möglich. Diese Art von Kommentaren sollte aber 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.
Neben der von Microsoft favorisierten Entwicklungsplattform VisualStudio.NET aus dem eigenen Hause, gibt es inzwischen auch Entwicklungsumgebungen (IDEs) anderer Hersteller für C#:
Konzept
Sprachelemente
using System;
|}
class HalloWelt
{
static void Main()
{
Console.WriteLine("Hallo Welt");
}
}Notationsregeln
Quellcode
Zeichensatz
Unicode
Schlüsselworte
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 whileif, elseif, else (Bedingte Ausführung)
if (Bedingung) {
Anweisungen;
}
[else if (Bedingung) {
Anweisungen;
}]
[else {
Anweisungen;
}]
for, while, do (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);
continue, break, goto
return, throw (Sprünge)
switch, case (Aufzählung)
Objekte und Klassen
new) einer Klasse. Eine Klasse kann man dabei als Bauplan oder Gerüst eines Objektes ansehen.Die Klasse object
Konstruktor
Destruktor
Das Schlüsselwort static
Das Schlüsselwort new
Das Schlüsselwort this
Schnittstellen (interface)
Strukturen (Struct)
Flags
Aufzählungstypen (Enumerationen)
Zugriffsmodifikatoren
internal ||
internal beschränkt den Zugriff auf Klassen und deren Mitglieder auf eine Assembly.
Im einfachsten Falle ist das die aktuelle Klasse (sofern kein Namespace definiert wurde), andernfalls ist der Zugriff auf den aktuellen Namespace und dessen Mitglieder beschränkt (unter Java entspricht das der Beschränkung auf ein package).
|----- valign="top"
| private ||
Beschränkt den Zugriff auf eine Klasse und deren Mitglieder. Eine mit private deklarierte
Klasse kann nur innerhalb der Klasse selbst instanziiert werden (beispielsweise kann ein öffentlicher Konstruktor einer Klasse, oder die statische Main-Methode, einen privaten Konstruktor aufrufen, der aber nicht von außen aufgerufen werden kann. Oft wird private verwendet, um das
Singleton-Pattern umzusetzen (z. B. bei der Verwendung einer Klasse als Fabrik; siehe Factory-Pattern) oder die Vererbung zu
beeinflussen oder zu verbieten (siehe auch Schlüsselwort sealed). Hinweis: Eine abgeleitetet Klasse kann auf private Mitglieder der Oberklasse (vgl. Schlüsselwort base) ebenfalls nicht zugreifen.
Soll ein solcher Zugriff möglich sein, so muss der Zugriffsmodifizierer protected verwendet werden.
|----- valign="top"
| protected ||
Dieser Modifikator ist mit dem Modifikator private identisch, bis auf einen Unterschied.
Sofern die Klasse nicht mit dem Schlüsselwort sealed gegen das Beerben "versiegelt" wurde,
können auch abgeleitete Klassen auf die Mitglieder einer Oberklasse (vgl. Schlüsselwort base) zugreifen oder
diese überschreiben. Ansonsten ist die Sichtbarkeit auf die Klasse selbst beschränkt.
|----- valign="top"
| public ||
Auf als public gekennzeichnete Klasse oder Klassenmitglieder (z. B. Methoden oder Eigenschaften), kann unbeschränkt zugegriffen werden. Sie werden deshalb auch als "öffentlich" bezeichnet. Eine Klasse die eine statische Main-Methode enthält oder sonstige statische Methoden, muss immer als public deklariert werden, da sonst kein Zugriff möglich ist und es zu einem Fehler kommt.
|}
Wurde kein Namensraum zugewiesen, so gilt der sog. "global declaration space" als Standard-Namensraum.private deklariert. Klassen selbst dagegen besitzen automatisch den Modifikator internal und sind nur im aktuellen Namensraum (Namespace) sichtbar.
protected und internal nicht kombiniert werden. protected internal spielt im Zusammenhang mit der Vererbung von Komponenten eine Rolle. Die Sichtbarkeit der beerbeten Klasse wird von der erbenden Klasse übernommen.public) ist nicht zulässig.Datentypen, Eigenschaften und Konstanten
// Datentyp Variable;
|}
int i;
System.Collections.IList liste;
// Datentyp Variable1, Variable2, ...;
|}
int i, j, k;
System.Collections.IList liste1, liste2;
// Datentyp Variable=Wert/Referenz;
|}
int i = 5;
int j = 2, k = 3;
System.Collections.IList liste = new System.Collections.ArrayList();
int i, j, k;
|}
i = j = k = 123;
Datentypen und Speicherbedarf
{| border="0"
|
Datentypen sind in C# nicht elementar, sondern objektbasiert. Jeder der in der Tabelle aufgeführten Datentypen stellt einen Alias auf eine Klasse des Namespaces System dar. Beispielsweise wird der Datentyp bool durch die Klasse System.Boolean abgebildet. Man spricht dabei auch von einer Wrapper-Klasse.
Durch die Objektbasiertheit ist es möglich, Methoden auf Datentypen anzuwenden, die die entsprechende Wrapper-Klasse bereitstellt:
{| style="background:#EEEEEE;"
|
1234.ToString();
|}
Vergleichbar mit C/C++, und anders als bei Java, gibt es unter C# vorzeichenbehaftete und vorzeichenlose Datentypen. Diese werden durch Voranstellen des Buchstaben s (signed) bei vorzeichenbehafteten Datentypen und durch Voranstellen des Buchstaben u (unsigned) bei vorzeichenlosen Datentypen symbolisiert (
int i = 17;
i.ToString();sbyte, uint, ulong, ushort, mit Ausnahme von short). Die Fließ- 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.
|
{| style="background:#DDEEFF;border:1px solid black;"
! Datentyp
! Bit
! Vorz.
! Wrapper-Klasse (struct type)
|-----
| bool || align="right" | 1
| align="center" | - || System.Boolean
|-----
| byte || align="right" | 8
| align="center" | J || System.Byte
|-----
| char || align="right" | 16
| align="center" | - || System.Char
|-----
| decimal || align="right" | 128
| align="center" | - || System.Decimal
|-----
| double || align="right" | 64
| align="center" | - || System.Double
|-----
| float || align="right" | 32
| align="center" | - || System.Single
|-----
| int || align="right" | 32
| align="center" | J || System.Int32
|-----
| long || align="right" | 64
| align="center" | J || System.Int64
|-----
| sbyte || align="right" | 8
| align="center" | J || System.SByte
|-----
| short || align="right" | 16
| align="center" | J || System.Int16
|-----
| uint || align="right" | 32
| align="center" | N || System.UInt32
|-----
| ulong || align="right" | 64
| align="center" | N || System.UInt64
|-----
| ushort || align="right" | 16
| align="center" | N || System.UInt16
|}
|}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.const sichert dabei nur, dass die Referenz
nicht veränderbar ist, aber schützt nicht vor Änderungen am referenzierten Objekt. So kann beispielsweise einer
Konstanten die Referenz auf eine Liste zugewiesen werden, aber es sind weiterhin Änderungen an der Liste
selbst möglich (z. B. das Hinzufügen oder das Löschen von Listenelementen).
{| style="background:#EEEEEE;"
|
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();
}
}
const existiert auch in anderen Sprachen (z. B. C++) oder es existieren
vergleichbare Schlüsselworte (z. B. final in der Programmiersprache Java oder PARAMETER in Fortran).Eigenschaften (Schlüsselworte get, set und value)
public zu deklarieren, weil somit unkontrolliert Wertänderungen durch Klasseninstanzen (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 kombinierbar.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).Wohnort für eine private Variable (_wohnort):
{| style="background:#eeeeee;"
|
public class EigenschaftBeispiel
|}
{
private string _wohnort;
public string Wohnort
{
get
{
return (_wohnort);
}
set
{
_wohnort = "12345 " + value;
}
}
}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).Wohnort:
{| style="background:#eeeeee;"
|
EigenschaftBeispiel instanz = new EigenschaftBeispiel();
|}
instanz.Wohnort = "Musterstadt";
System.Console.WriteLine(instanz.Wohnort);
// Ausgabe: 12345 Musterstadt
get-Block weggelassen, so würde
der lesende Zugriff zu einem Zugriffsfehler führen (im Beispiel in der Zeile, in der die Ausgabe erfolgt).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.Die Klasse string
Escape-Sequenzen
Methoden
Parameter
Die Schlüsselworte in, out, ref
Parameterlisten
Rückgabewert
Indexer und Arrays (Felder)
Ereignisse und Delegate
Operatoren
Sichtbarkeit
Namensräume (Namespaces)
Schlüsselwort using
Vererbung
Abstrakte Klassen
Interfaces
Das Schlüsselwort override
Das Schlüsselwort base
Das Schlüsselwort sealed
Casting (Konvertierung)
Überladen vs Überschreiben
Mehrfachvererbung
Exception-Handling (Ausnahmen)
Assemblies
Attribute (Metadaten)
Shared Libraries (DLLs)
Kommentare und Dokumentation
|}
Blockkommentar */System.Console.WriteLine("Noch einer");
Verfügbare Klassenbibliotheken
Verfügbarkeit (IDEs)
Weblinks