Jeder Typ in .NET hat einen vollständigen Namen aus einem Namespace und dem Typnamen: System.Console, System.Collections.Generic.List<T>, System.Text.StringBuilder. Namespaces gruppieren zusammengehörige Typen und verhindern Namenskonflikte, sodass deine eigene Klasse Order oder Timer neben denen aus Bibliotheken existieren kann. Die using-Direktive am Anfang einer Datei importiert einen Namespace, damit du die kurzen Namen verwenden kannst.
Ausgabe:
Cart: tea, honey
1
Eine using-Direktive wirkt nur zur Kompilierzeit auf Namen. Sie lädt keine Bibliothek und fügt dem Programm keinen Code hinzu; welche Bibliotheken verfügbar sind, bestimmen die Referenzen und NuGet-Pakete des Projekts. Meldet der Compiler, dass ein Typ oder Namespace „could not be found (are you missing a using directive or an assembly reference?)“, Fehler CS0246, dann fehlt entweder die using-Zeile oder das Paket ist nicht referenziert.
Eigene Namespaces deklarieren
Setze Typen in einen namespace-Block. Namespaces lassen sich verschachteln und mit Punkten schreiben, und derselbe Namespace kann sich über viele Dateien verteilen.
Ausgabe:
120.00 via DHL
Shop.Billing.Invoice
namespace Shop.Billing { } ist die Kurzform von namespace Shop { namespace Billing { } }. Code innerhalb von Shop kann Billing.Invoice ohne das Präfix Shop. ansprechen, weil Namen zuerst in den umgebenden Namespaces gesucht werden.
Konventionen: Beginne mit der Firma oder dem Produkt (Contoso.Payments), dann folgt das Feature oder die Schicht, und halte dich an die Ordnerstruktur (Contoso.Payments/Refunds/RefundService.cs enthält Contoso.Payments.Refunds). Benenne eine Klasse nie wie ihren Namespace (Shop.Shop), das erzwingt überall umständliche Qualifizierung.
Aliase
using Name = Namespace.Type; gibt einem Typ (oder einem Namespace) einen lokalen Namen. Zwei Situationen verlangen danach: einen langen generischen Typ abkürzen und einen Konflikt auflösen.
Ausgabe:
latte: 3.60
order 17
Aliase gelten nur in der Datei, in der sie deklariert sind (außer sie sind global, siehe unten). Ab C# 12 kann ein Alias jeden Typ benennen, auch Tupel und Arrays: using Point = (int X, int Y);.
Mehrdeutige Verweise (CS0104)
Wenn zwei importierte Namespaces einen Typ mit demselben Namen enthalten, ist der kurze Name ein Fehler. Der klassische Fall ist Timer, den es in System.Threading, System.Timers und (in Desktop-Apps) System.Windows.Forms gibt:
using System.Threading;
using System.Timers;
var t = new Timer(1000);
// error CS0104: 'Timer' is an ambiguous reference between
// 'System.Timers.Timer' and 'System.Threading.Timer'
Drei Lösungen, von der lokalsten zur allgemeinsten: den vollqualifizierten Namen schreiben (new System.Timers.Timer(1000)), einen Alias anlegen, der sagt, welcher gemeint ist, oder die nicht benötigte Direktive entfernen.
Ausgabe:
250
System.Timers.Timer
System.Threading.Timer
Wenn dein eigener Namespace einen Typ namens System oder Console definiert, erzwingt das Präfix global:: die Suche ab der Wurzel: global::System.Console.WriteLine("hi");. Generierter Code verwendet es aus diesem Grund überall.
using static
using static importiert die statischen Member eines einzelnen Typs, sodass sie ohne den Typnamen aufgerufen werden können. Das passt zu Code, der immer wieder dieselbe Klasse aufruft, wie Math:
Ausgabe:
Area: 28.27
Hypotenuse: 5
42
using static funktioniert auch mit Enums (using static System.DayOfWeek; erlaubt dir, Monday zu schreiben) und mit deinen eigenen statischen Klassen. Nutze es dort, wo die kurzen Namen eindeutig bleiben; ein alleinstehendes WriteLine in einer großen Datei kann Leser rätseln lassen, welcher Writer gemeint ist.
global using und dateibezogene Namespaces (C# 10)
Zwei Features aus C# 10 reduzieren den Boilerplate am Dateianfang. Ein global using gilt für jede Datei im Projekt und steht meist in einer eigenen Datei wie GlobalUsings.cs:
// GlobalUsings.cs, C# 10 and later
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using Money = System.Decimal;
SDK-Projekte ab .NET 6 können diese für dich erzeugen. Mit <ImplicitUsings>enable</ImplicitUsings> in der .csproj (der Standard in neuen Vorlagen) bekommt ein Konsolenprojekt System, System.Collections.Generic, System.IO, System.Linq, System.Net.Http, System.Threading und System.Threading.Tasks; Webprojekte bekommen zusätzlich die Namespaces von ASP.NET Core. Deshalb beginnen moderne Program.cs-Dateien direkt mit Code.
Ein dateibezogener Namespace deklariert den Namespace für die ganze Datei mit einem Semikolon statt eines Blocks und spart eine Einrückungsebene:
// C# 10 and later
namespace Shop.Billing;
public class Invoice
{
public decimal Amount { get; set; }
}
Eine Datei kann nur einen dateibezogenen Namespace haben, und er lässt sich in derselben Datei nicht mit Block-Namespaces mischen. Ältere Projekte, Unity-Skripte und Code für .NET Framework verwenden die weiter oben gezeigte Blockform.
Wo using-Direktiven stehen, und die using-Anweisung
Using-Direktiven müssen in der Datei vor allen Namespace- oder Typdeklarationen stehen (globale Usings vor allem anderen). Sie können auch in einem Namespace-Block stehen und gelten dann nur dort; Styleguides sind sich da uneinig, und der meiste .NET-Code hält sie am Anfang.
Das Schlüsselwort using hat innerhalb von Methoden eine zweite, davon unabhängige Aufgabe: Die using-Anweisung using (var file = File.OpenRead(path)) { ... } ruft Dispose für das Objekt auf, wenn der Block endet, und schließt so Dateien und Verbindungen. Sie teilt sich das Schlüsselwort und sonst nichts. Siehe die Seite zur using-Anweisung.
Häufig gestellte Fragen
Was macht using in C#?
Am Anfang einer Datei ist using System.Text; eine Direktive, die einen Namespace importiert, sodass du StringBuilder statt System.Text.StringBuilder schreiben kannst. Sie fügt keinen Code hinzu und lädt nichts; sie verkürzt nur Namen. Innerhalb einer Methode ist using (var f = ...) ein anderes Feature, die using-Anweisung, die ein Objekt am Ende eines Blocks freigibt.
Was ist ein Namespace in C#?
Ein Namespace ist eine benannte Gruppe von Typen, die Namenskonflikte verhindert und Code organisiert, etwa System.Collections.Generic oder MyShop.Billing. Zwei Klassen dürfen denselben Namen haben, wenn sie in verschiedenen Namespaces liegen. Per Konvention folgen Namespaces der Ordnerstruktur und beginnen mit dem Firmen- oder Produktnamen.
Was ist using static in C#?
using static System.Math; importiert die statischen Member eines Typs, sodass du Sqrt(16) aufrufen und PI lesen kannst, ohne das Präfix Math.. Das funktioniert mit jeder Klasse, auch deinen eigenen, und mit Enums. Nutze es dort, wo die kurzen Namen klar bleiben, etwa in mathelastigem Code.
Wie behebe ich „is an ambiguous reference“ in C#?
Fehler CS0104 bedeutet, dass zwei importierte Namespaces beide einen Typ mit diesem Namen enthalten, zum Beispiel Timer in System.Threading und System.Timers. Schreibe den vollqualifizierten Namen (System.Timers.Timer), entferne eine der using-Direktiven oder lege einen Alias an: using Timer = System.Timers.Timer;.
Was ist global using in C#?
global using System.Text; (C# 10) importiert einen Namespace für jede Datei im Projekt, nicht nur für die, in der es steht. SDK-Projekte ab .NET 6 erzeugen automatisch eine Reihe globaler Usings, wenn <ImplicitUsings>enable</ImplicitUsings> gesetzt ist. Deshalb haben neue Program.cs-Dateien keine using-Zeilen.