Dateibasierte C#-Ausführung in .NET 10
[[academy-video-youtube({"vid": "2i0MJDHvJq0", "start_time": "0", "title": "File-Based C# Execution in .NET 10", "creator": "Tim Corey", "length": "13m 36s"})]]
C# hat schon immer eine Projektdatei benötigt, um Code auszuführen. Selbst das einfachste "Hello World" benötigte einen .csproj, einen Program.cs und einen Build-Schritt, bevor irgendetwas ausgeführt wurde. Mit .NET 10 ändert sich das. Sie können jetzt eine einzelne .cs-Datei schreiben und direkt ausführen, genauso wie Sie ein Python-Skript oder eine Node.js-Datei ausführen würden.
In seinem Video "File-Based C# Execution in .NET 10" erklärt Tim Corey jedes Detail dieses Features: das Ausführen einer eigenständigen Datei, das Übergeben von Befehlszeilenargumenten, das Hinzufügen von NuGet-Paketen inline, das Veröffentlichen als native ausführbare Datei und das Umwandeln einer Datei in ein vollständiges Projekt, wenn der Umfang das Einzeldateiformat übersteigt. Wenn Sie Konsolen-Apps für schnelle Skripte und Prototypen verwendet haben, ändert dies den Workflow erheblich.
Ausführen einer einzelnen C#-Datei
[0:40 - 1:48] Tim beginnt in VS Code mit einem leeren Ordner. Keine Lösung, keine Projektdatei, kein Boilerplate. Er erstellt eine einzelne Datei namens demo.cs mit einer Codezeile:
Console.WriteLine("Hello World");
Console.WriteLine("Hello World");
Um sie auszuführen:
dotnet run file demo.cs
dotnet run file demo.cs
Das ist der vollständige Workflow. Der dotnet run file-Befehl kompiliert und führt die .cs-Datei in einem Schritt aus. Es wird kein intermediärer .csproj erzeugt, keine bin- oder obj-Ordner erstellt. Das Denkmodell ist näher am Skripting als an der traditionellen C#-Entwicklung: eine Datei schreiben, ausführen, Ausgabe sehen.
Tim zieht einen direkten Vergleich zu Python und JavaScript, wo die Ausführung einer einzelnen Datei immer der Standard war. .NET 10 bringt C# in dieses Gebiet, während die Typsicherheit und Leistung beibehalten werden, die C# attraktiv machen.
Befehlszeilenargumente und implizite Usings
[1:48 - 3:26] Das args-Array ist automatisch verfügbar, genauso wie es in einem Standard-Program.cs mit Top-Level-Anweisungen der Fall wäre. Tim ändert die Datei, um ein Name-Argument zu akzeptieren:
Console.WriteLine($"Hello {args[0]}");
Console.WriteLine($"Hello {args[0]}");
Ausführen mit einem Argument:
dotnet run file demo.cs Tim
dotnet run file demo.cs Tim
Dies druckt "Hello Tim". Tim weist darauf hin, dass Console.WriteLine ohne eine using System;-Anweisung funktioniert, da die dateibasierte Ausführung standardmäßig implizite Usings einschließt. Dies ist das gleiche Verhalten, das die C# 9 Top-Level-Anweisungen eingeführt haben, jetzt auf eigenständige Dateien erweitert.
Das umfassendere Muster, dem Microsoft gefolgt ist, reduziert in jeder Version den Formalismus von C#. Globale Usings, Top-Level-Anweisungen und jetzt eigenständige .cs-Dateien sind alles Schritte, um C# für schnelle Aufgaben nutzbar zu machen, die zuvor Python- oder Bash-Skripte erforderten.
Hinzufügen von Benutzereingaben
[3:26 - 5:43] Tim ersetzt den Argumentansatz durch interaktive Eingaben, um zu demonstrieren, dass dateibasierte Ausführung das volle Spektrum an Konsolenanwendungs-Mustern unterstützt:
Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");
Console.Write("What is your name? ");
string name = Console.ReadLine();
Console.WriteLine($"Hello {name}");
Das Ausführen hiervon fordert zur Eingabe auf, liest die Antwort und druckt die Begrüßung aus. Standard-I/O funktioniert identisch wie ein vollständiges Projekt.
Eine Einschränkung, die Tim hervorhebt: Dieser Modus ist streng ein Datei-basiert. Sie können nicht zwei .cs-Dateien haben, die sich gegenseitig referenzieren. Wenn Ihr Code wächst und Sie mehrere Dateien benötigen, ist es an der Zeit, in ein Projekt umzuwandeln (später im Video behandelt).
Einbinden von NuGet-Paketen
[5:43 - 7:49] Hier wird der Ansatz mit einer einzigen Datei tatsächlich nützlich für das Scripting. Sie können direkt innerhalb der .cs-Datei NuGet-Pakete referenzieren, indem Sie am Anfang eine #r-Direktive nutzen:
#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;
Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");
#r "nuget:Spectre.Console, 0.54.0"
using Spectre.Console;
Console.Write("What is your name? ");
string name = Console.ReadLine();
AnsiConsole.MarkupLine($"Hello [red]{name}[/]");
Die #r "nuget:..."-Syntax weist die Laufzeit an, das angegebene Paket vor der Kompilierung herunterzuladen und zu referenzieren. Tim verwendet Spectre.Console, um die Ausgabe zu färben und den Begrüßungstext rot zu stellen.
Kein dotnet add package-Befehl, kein .csproj zum Bearbeiten, kein Wiederherstellungsschritt. Der Paketverweis befindet sich direkt in der Quelldatei. Für Skripte, die einen HTTP-Client, einen JSON-Serializer oder eine Formatierungsbibliothek benötigen, entfällt der Aufwand, ein Projekt nur für das Heranziehen einer Abhängigkeit zu erstellen.
Veröffentlichen zu einem nativen ausführbaren Programm
[7:49 - 8:59] Wenn Sie ein dateibasiertes Skript als eigenständige Binärdatei verteilen möchten, übernimmt der Veröffentlichungskommand das:
dotnet publish file demo.cs
dotnet publish file demo.cs
Dies erzeugt eine ausführbare Datei im artifacts-Ordner. Die kompilierte Binärdatei enthält alles, was zum Ausführen benötigt wird, ohne dass das .NET SDK auf der Zielmaschine erforderlich ist. Tim bemerkt, dass dateibasierte Builds standardmäßig Native AOT verwenden, was bedeutet, dass das Ergebnis eine einzige, eigenständige Binärdatei mit schnellen Startzeiten ist.
Wenn Native AOT Kompatibilitätsprobleme mit einer bestimmten Bibliothek verursacht, können Sie es deaktivieren, indem Sie eine Property-Direktive am Anfang der Datei hinzufügen (ähnlich wie #r bei Paketen funktioniert). Für die meisten Skriptverwendungsfälle ist jedoch AOT die richtige Standardeinstellung.
Umwandlung in ein vollständiges Projekt
[8:59 - 10:45] Der Ausweg, wenn ein Skript das Ein-Datei-Format überschreitet, ist der Umwandlungsbefehl:
dotnet project convert file demo.cs
dotnet project convert file demo.cs
Dies erzeugt eine .csproj-Datei, die die NuGet-Paket-Referenzen aus den #r-Direktiven enthält und den Code in eine Standard-Projektstruktur verschiebt. Ab diesem Punkt können Sie mehrere Dateien hinzufügen, Build-Einstellungen konfigurieren und das vollständige .NET-Projektsystem verwenden.
Tim bezeichnet dies als natürlichen Fortschritt: Starten Sie mit einer einzigen Datei für schnelle Experimente, und wenn der Umfang zunimmt, machen Sie daraus ein Projekt, ohne etwas neu zu schreiben. Die #r-Direktiven übersetzen sich sauber in <PackageReference>-Einträge in der erzeugten .csproj.
Native AOT und Plattformhinweise
[10:45 - 12:42] Standardmäßig wird die dateibasierte Ausführung mit Native AOT kompiliert, was die schnellstmöglichen Startzeiten erzeugt. Tim merkt an, dass dies ideal für CLI-Tools und Skripte ist, bei denen die Kaltstartleistung wichtig ist. Wenn eine Bibliothek nicht AOT-kompatibel ist, kann das Feature mit einer Dateiebene-Eigenschaft deaktiviert werden.
Unter Linux und macOS können Sie auch eine Hashbang (#!/usr/bin/dotnet run file) am Anfang der .cs-Datei hinzufügen, wodurch sie direkt aus der Shell ohne Eingabe des dotnet run file-Präfixes ausführbar ist. Dies bringt C# vollständig in das Scripting-Gebiet neben Bash, Python und Ruby.
Abschluss: C# als Skriptsprache
[12:42 - 13:05] Die Funktion, die Tim als am einflussreichsten hervorhebt, ist die Beseitigung des Projektaufwands für kleine Aufgaben. Konsolenanwendungen waren schon immer das Mittel der Wahl für schnelle Experimente, Automatisierungsskripte und einmalige Tools. Die dateibasierte Ausführung entfernt den Aufwand, der diese schwerer machte als nötig, während alle Vorteile (Typsicherheit, Leistung, NuGet-Ökosystem) erhalten bleiben, die C# zur Wahl gegenüber einer Skriptsprache machen.
Abschluss
[13:05 - 13:36] Zusammengefasst: Die dateibasierte Ausführung von .NET 10 erlaubt es Ihnen, eine einzelne .cs-Datei mit dotnet run file auszuführen, NuGet-Pakete mit #r-Direktiven zu referenzieren, in eine native Binärdatei mit dotnet publish file zu veröffentlichen und in ein vollständiges Projekt mit dotnet project convert file zu konvertieren, wenn Sie das Ein-Datei-Format überschreiten. Native AOT ist standardmäßig aktiviert, und Linux/macOS-Nutzer erhalten Unterstützung für Hashbangs zur Ausführung auf Shell-Ebene.
Für alles, wofür Sie derzeit eine Konsolenanwendung zum Prototyping oder zur Automatisierung verwenden, ist dies einen Versuch wert als eine leichtere Alternative.
Beispieltipp: Wenn Sie sich dabei erwischen, Konsolenanwendungen zu erstellen, nur um ein NuGet-Paket zu testen oder einen API-Aufruf zu debuggen, versuchen Sie stattdessen die dateibasierte Ausführung. Erstellen Sie eine .cs-Datei, fügen Sie #r "nuget:PackageName, Version" am Anfang hinzu, schreiben Sie Ihren Testcode und führen Sie ihn mit dotnet run file aus. Wenn Sie fertig sind, löschen Sie die Datei. Kein Projektbereinigungsaufwand erforderlich.
Schauen Sie das vollständige Video auf seinem YouTube Kanal an und erhalten Sie weitere Einblicke in moderne C#-Entwicklungsworkflows.
