using System; public class TestAusgabe { public void Ausgabe() { Console.WriteLine("Ohne Namespace..."); } }Listing 6.1: OhneNamespace.cs (Projekt MitUndOhneNamespaceProj)
Waehrungen
(oder Currencies
) befinden.
Die Klasse TestAusgabeNS
wird im Namespace MitNamespace
untergebracht. Der vollständige Name der Klasse lautet nun MitNamespace.TestAusgabeNS
.
using System; namespace MitNamespace { public class TestAusgabeNS { public void Ausgabe() { Console.WriteLine("Mit Namespace..."); } } }Listing 6.2: MitNamespace.cs (Projekt MitUndOhneNamespaceProj)
TestAusgabe
zu verwenden, ist keine weitere Aktion notwendig. Entweder Sie binden die Source-Datei oder die Assembly in das Projekt ein.
Anders sieht es beim Zugriff auf die Klasse TestAusgabeNS
aus. Entweder Sie binden den Namespace MitNamespace
über die using
-Anweisung ein
(jetzt können Sie wieder direkt den Klassennamen verwenden) oder Sie verwenden die Klasse mit ihrem voll qualifizierten Namen MitNamespace.TestAusgabeNS
.
Letzteres ist zum Beispiel dann notwendig, wenn sich eine gleichnamige Klasse in mehreren eingebundenen Namespaces befindet.
using System; using MitNamespace; namespace CSharpBuch.Kap06 { public class MitUndOhneNamespace { public static void Main(string[] args) { new TestAusgabe().Ausgabe(); // verkürzte Schreibweise, da Namespace eingebunden new TestAusgabeNS().Ausgabe(); // vollständig qualifizierte Schreibweise new MitNamespace.TestAusgabeNS().Ausgabe(); Console.ReadLine(); } } }Listing 6.3: MitUndOhneNamespace.cs (Projekt MitUndOhneNamespaceProj)
class
)
interface
)
struct
)
enum
)
delegate
)
Wird kein Namespace verwendet, wird dieser als globaler, unbenannter oder Standard-Namespace bezeichnet. Die darin deklarierten Bezeichner können überall verwendet werden.
INFO
Natürlich können Sie statt eines Namespaces den Klassennamen aus dem Namen des Namespaces und dem Klassennamen zusammensetzen. Der Quellcode würde allerdings auf diese Weise schlechter lesbar sein, wenn statt dem KlassennamenMathe
der Name CSharpBuchKap06Mathe
verwendet werden
würde. Außerdem wäre dadurch die Nutzung der Klasse CSharpBuchKap06Mathe
im Kapitel 5 aufgrund ihres Namens sehr verwirrend.Namespace | Einsatzgebiet der darin enthaltenen Typen |
DFSoftware.Utils.Mathe | Hilfsmethoden für mathematische Operationen |
DFSoftware.Taschenrechner | Hauptanwendung - ein Taschenrechner |
DFSoftware.Web.Update | Methoden, um die Anwendung über das Web zu aktualisieren |
Innerhalb des Namespaces CSharpBuch
werden sämtliche anderen Typen und Namespaces untergebracht, die irgendetwas mit diesem Buch zu tun haben.
Zum einfacheren Auffinden der Beispiele, wird für jedes Kapitel ein eigener Unter-Namespace mit dem Kapitelnamen verwendet. Es wäre es auch möglich gewesen,
nochmals pro Abschnitt einen eigenen Namespace Unterkapitel
mit einer fortlaufenden Nummerierung zu deklarieren. Über den Projektnamen sind die
Anwendungen aber bereits ausreichend eindeutig gekennzeichnet.
using System; namespace CSharpBuch { namespace Kap06 { namespace Unterkapitel { public class Test { static void Main(string[] args) { // Kurzschreibweise von // Test t = new CSharpBuch.Kap06.Unterkapitel.Test(); // t.Ausgabe(); new CSharpBuch.Kap06.Unterkapitel.Test().Ausgabe(); // natürlich könnte die Klasse Test auch unqualifiziert // verwendet werden // new Test().Ausgabe(); } public void Ausgabe() { Console.WriteLine("Verschachtelung von Namespaces"); Console.ReadLine(); } } } } }Listing 6.4: VerschachtelteNamespaces.cs
namespace
eingeleitet. Zugriffsmodifizierer kommen bei Namespaces nicht zum Einsatz. Ihr Inhalt ist immer public
.
namespace CSharpBuch { namespace Kap06 { namespace Unterkapitel {lässt sich auch verkürzt schreiben:
namespace CSharpBuch.Kap06.Unterkapitel {Innerhalb einer C#-Datei können Sie auch mehrere Namespace-Deklarationen verwenden. Allerdings ist dies unüblich, da pro Datei in der Regel nur ein Typ definiert wird.
namespace Namespace1 {} namespace Namespace2 {}Ein Namespace kann sich durchaus über mehrere Dateien und Assemblies erstrecken. So können Sie beispielsweise eine Assembly erstellen, welche die wichtigsten Klassen eines Namespaces umfasst und eine weitere Assembly, die spezielle Hilfsklassen enthält. Der Inhalt des Namespaces ist die Summe aller Typdefinitionen in allen diesen Dateien. In diesem Fall müssen Sie darauf achten, dass jeder Bezeichner nur einmal innerhalb des Namespaces vorkommt. Ansonsten erhalten Sie einen Compilerfehler, da der Typname nicht mehr eindeutig ist.
INFO
Der Name eines Namespaces ist völlig unabhängig vom Namen oder dem Speicherort einer Datei. So kann sich der Inhalt eines Namespaces aus mehreren Dateien zusammensetzen, die an völlig unterschiedlichen Orten gespeichert sind. Namespaces dienen nur dazu, die darin enthaltenen Typen zu strukturieren.DFSoftware.Matrizen
heißen.
Namespaces werden wie Typnamen in der Pascalschreibweise bezeichnet, d.h. jedes Hauptwort beginnt mit einem Großbuchstaben. Verwenden Sie möglichst einen mehrstufigen Namespace
für Ihre Typen, der z.B. aus dem Firmen-, Familien- oder Produktnamen und mindestens einer Unterkategorie besteht. Hilfsklassen können Sie z.B. in einem Namespace Util
(für Utility - Hilfsklassen) unterbringen. Die Klassen einer Anwendung gelangen dann in einen Namespace, der dem Namen der Anwendung entspricht, z.B.
DFSoftware.DFWord DFSoftware.Taschenrechner
using
-Direktive bekannt.
// voll qualifizierten Namen verwenden CSharpBuch.Kap06.Test t = new CSharpBuch.Kap06.Test(); // oder Namespace einbinden using CSharpBuch.Kap06; ... Test t = new Test();Der Direktive
using
folgt der vollständige Name des Namespaces. Hierbei ist zu beachten, dass exakt nur der angegebene Namespace eingebunden wird. Die
Reihenfolge bei der Verwendung mehrerer using
-Direktiven ist nicht signifikant. Mit anderen Worten - eine using
-Direktive hat keinen Einfluss auf eine andere using
-Direktive.
Besitzt der Namespace weitere verschachtelte Unter-Namespaces, müssen diese separat eingebunden werden. Die Verwendung von Platzhalterzeichen wie dem Stern * ist nicht möglich.
using System; using System.Text;Die
using
-Direktive kann sich außerhalb oder innerhalb einer Namespace-Deklaration befinden. Befindet sie sich darin, ist sie auch nur im Gültigkeitsbereich des
Namespaces aktiv. Dies ist die bevorzugte Schreibweise laut Code-StyleGuide. Allerdings befindet sich in einer Datei normalerweise sowieso nur ein Namespace, so dass der
Autor es bevorzugt die using
-Anweisungen immer zu Beginn vor dem Namespace anzugeben.
Die using
-Direktive muss sich aber in jedem Fall vor allen anderen Typdefinitionen befinden.
using
-Direktive:
using System; namespace CSharpBuch {
using
nur innerhalb des Namespaces und Unter-Namespaces
namespace CSharpBuch { using System;
using
muss vor allen Typdefinitionen stehen
namespace CSharpBuch { public class Test ... ... using System; // Fehler
INFO
Der Zugriff auf einen Typ kann nur über den voll qualifizierten Namen oder den reinen Typnamen (unqualifizierter Name) erfolgen. Es erfolgt keine Zusammensetzung von unvollständigen Bezeichnernusing System; ... // Der Compiler erzeugt einen Fehler, da es keinen Namespace // Text gibt. Eine automatische Zusammensetzung von // System.Text.StringBuilder ist nicht möglich Text.StringBuilder sb = new Text.StringBuilder();
using
-Direktive folgt zur Definition eines Alias ein Bezeichner und mit einem Gleichheitszeichen getrennt der Name eines Namespaces oder
eines voll qualifizierten Typs. Die Definition eines Alias muss vor allen Typdeklarationen erfolgen, also wie beim üblichen Gebrauch der using
-Direktive.
using K6Test = CSharpBuch.Kap06.Test; using Kap6 = CSharpBuch.Kap06; ... K6Test t = new K6Test(); Kap6.Test t = new Kap6.Test();
INFO
Befinden Sie sich in einem bestimmten Namespace, können Sie implizit auf alle Bezeichner über deren unqualifizerten Namen zugreifen, die in diesem Namespace definiert werden.ACHTUNG
Das Einbinden zweier Namespaces, die den gleichen Bezeichner enthalten erzeugt noch keinen Compiler-Fehler. Erst die unqualifizierte Verwendung eines Bezeichners wird vom Compiler bemängelt, da er nicht feststellen kann, welcher gemeint ist. Ein Ausweg besteht in der Verwendung des voll qualifizierten Namens bei der Erzeugung eines Objekts.using NameSpaceA; // enthält den Typ Test using NameSpaceB; // enthält auch den Typ Test ... Test t = new NameSpaceB.Test();
In der Anwendung wird für den Namespace MitNamespace
und den Typ MitNamespace.TestAusgabe
jeweils ein Alias definiert. Außerdem befindet
sich der Typ TestAusgabe
noch einmal ohne Namespace in der Anwendung. Über new
wird dann dreimal ein Objekt vom betreffenden Typ erzeugt und sofort im
Anschluss über den Punkt die Methode Ausgabe()
aufgerufen. Dies ist also auch ein Beispiel, welches die Objekterzeugung mit einem sofortigen Methodenaufruf verbindet.
Allerdings haben Sie in diesem Fall keinen Zugriff auf das Objekt, da es keiner Referenzvariablen zugewiesen wird. Zuerst wird die Methode Ausgabe()
der Klasse
TestAusgabe
aufgerufen, die sich in keinem Namespace befindet. Dann wird zweimal die Methode Ausgabe()
der Klasse verwendet, die im Namespace
MitNamespace
untergebracht ist.
using System; using MN = MitNamespace; using MNC = MitNamespace.TestAusgabe; namespace NamespaceAliase { public class NamespaceAliase { public static void Main(string[] args) { new TestAusgabe().Ausgabe(); new MN.TestAusgabe().Ausgabe(); new MNC().Ausgabe(); Console.ReadLine(); } } } namespace MitNamespace { public class TestAusgabe { public void Ausgabe() { Console.WriteLine("Mit Namespace..."); } } } public class TestAusgabe { public void Ausgabe() { Console.WriteLine("Ohne Namespace..."); } }Listing 6.5: NamespaceAliase.cs
global
steht für die Wurzel aller Typen und Namespaces der globalen Umgebung. Bei der Angabe von global
links vom Namespace wird die Suche
nach einem Typ im globalen (unbenannten) Namespace begonnen. Das .NET Framework referenziert global::System
z.B. als den System
-Namespace. Die Angabe von
global
ist nicht notwendig, wenn es keine Überlagerungen von Namen gibt. Wenn Sie jedoch auf gleichnamige Member eines anderen Namespaces zugreifen möchten müssen Sie
den voll qualifizierten Namen von global
aus angeben.
Der Namespace-Aliasqualifizierer ::
(ein weiterer Operator) wird verwendet, um einen Alias (für Namespaces) oder den globalen Namensraum zu referenzieren.
Sie definieren in Ihrer Klasse zwei Variablen mit den Namen System
und Console
(was man natürlich tunlichst vermeiden sollte – aber auf Bibliotheken
fremder haben Sie z.B. keinen Einfluss). Dann führt der Aufruf der Methode WriteLine()
über Console.WriteLine()
sowie mittels System.Console.WriteLine()
bereits bei der Kompilierung zu einem Fehler, da Sie die Methode offensichtlich über die Variablen versuchen aufzurufen. Sie müssen dann den vollen Namen, angefangen von der
Wurzel global angeben. Durch den ::
-Operator wird global
mit dem folgenden Namespace bzw. Member verbunden.
global::System.Console.WriteLine("der ::-Operator");
TIPP
Sie sollten in der Regel Bezeichner so wählen, dass sie nicht mit den Namen des NamespacesSystem
oder anderen Namespaces übereinstimmen.
Andernfalls wird ihr Code schwerer verständlich und schlechter zu warten. Auch Fehler lassen sich dann mitunter schwerer finden.
Der Zugriff über global
wird in der Regel nur dann benötigt, wenn in einer neuen Version des .NET Frameworks neue Namespaces hinzugekommen sind, die dann eventuell zu
Überschneidungen mit bereits existierenden Bezeichnern führen.::
-Operator angewandt werden. Für obiges Beispiel aus dem Abschnitt Aliase wäre auch der folgende Aufruf möglich:
new MN::TestAusgabe().Ausgabe();
INFO
::
-Operator kann nicht für Aliase verwendet werden, die für einen Typ stehen, z.B.
using MNC = MitNamespace.TestAusgabe;
using
-Direktive zum Einbinden von Namespaces existiert außerdem die using
-Anweisung die dazu dient, für ein Objekt einen begrenzten
Gültigkeitsbereich zu schaffen. Dazu wird die using
-Anweisung direkt im Code eingesetzt. In runden Klammern wird eine Instanz einer Klasse erzeugt.
Diese ist nur innerhalb des folgenden Anweisungsblocks gültig. Der Typ des erzeugten Objekts muss die Schnittstelle IDisposable
implementieren, da beim Verlassen des
Gültigkeitsbereichs automatisch die Methode Dispose()
aufgerufen wird (mehr dazu im Kapitel zu Interfaces). Implementiert der Typ diese Schnittstelle nicht,
meldet bereits der Compiler einen Fehler.
Direkt hinter der using
-Anweisung wird in runden Klammern ein neues Objekt erzeugt, dessen Gültigkeitsbereich sich nur auf den folgenden Anweisungsblock der using
-Anweisung
beschränkt. Die Klasse TestObjekt
implementiert das Interface IDisposable
was im konkreten Fall heißt, dass sie eine Methode Dispose()
mit der angegebenen Signatur
besitzt. Beim Verlassen des Gültigkeitsbereichs der using
-Anweisung wird automatisch die Methode Dispose()
aufgerufen, die noch vor der Methode ReadLine()
,
also unmittelbar nach Verlassen des Blocks, ausgeführt wird. Damit stellen Sie von Ihrer Seite aus sicher, dass ein Objekt nur solange seine Ressourcen beansprucht wie notwendig.
Diese Ressourcen sollten in der Methode Dispose()
freigegeben werden.
using System; namespace CSharpBuch.Kap06 { public class BeschraenkteGueltigkeit { public static void Main(string[] args) { using(TestObjekt to = new TestObjekt()) { to.Ausgabe(); } Console.ReadLine(); } } } public class TestObjekt : IDisposable { public void Ausgabe() { Console.WriteLine("Halli Hallo"); } public void Dispose() { Console.WriteLine("Aufräumen mit Dispose"); } }Listing 6.6: BeschraenkteGueltigkeit.cs
using
-Anweisung. Rufen Sie im Anweisungsblock dieser Anweisung die Methode der Klasse auf.