4 Punkte von GN⁺ 2025-05-30 | 4 Kommentare | Auf WhatsApp teilen
  • Seit .NET 10 Preview 4 gibt es nun die Möglichkeit, eine einzelne C#-Datei direkt mit dotnet run app.cs auszuführen, sodass C#-Code auch ohne Projektdatei ausgeführt werden kann
  • Dank dateibasierter Apps (file-based apps) werden einfache Skriptausführung, Tests und das Ausprobieren von Ideen ähnlich wie in Python oder JavaScript deutlich einfacher
  • Auch NuGet-Paketverweise, SDK-Angaben und Build-Eigenschaften lassen sich über Direktiven in der Datei verwalten, was die Entwicklungsflexibilität erhöht
  • Mit shebang-Unterstützung ist die Nutzung unter Unix-artigen Systemen auch für CLI-Utilities und Automatisierungsskripte möglich
  • Bei Bedarf ist eine nahtlose Umwandlung in eine projektbasierte App möglich, sodass sich der Weg vom Lernen und Prototyping bis zur vollwertigen App-Entwicklung natürlich fortsetzen lässt

Was ist dotnet run app.cs?

  • Bisher war zum Ausführen von C#-Code mit der dotnet-CLI zwingend eine Projektstruktur (.csproj) erforderlich
  • Jetzt ist die direkte Ausführung schon mit einer einzelnen .cs-Datei möglich, was die Einstiegshürde deutlich senkt
  • Das eignet sich für viele Einsatzfälle wie Skriptsprachen, Automatisierung, Experimente und Lernen
  • Durch die CLI-Integration kann es sofort mit dotnet verwendet werden, ohne zusätzliche Tools zu installieren
  • Wenn der Code wächst, lässt er sich mit derselben Sprache und denselben Tools zu einer projektbasierten App erweitern

Unterstützung für Direktiven auf Dateiebene

  • Auch in dateibasierten Apps lassen sich zentrale Projekteinstellungen direkt als Direktiven in der .cs-Datei deklarieren
  • NuGet-Paketverweise

    • Mit der Direktive #:package können NuGet-Pakete direkt referenziert werden
      • Beispiel:
        #:package Humanizer@2.14.1  
        
        using Humanizer;  
        
        var dotNet9Released = DateTimeOffset.Parse("2024-12-03");  
        var since = DateTimeOffset.Now - dotNet9Released;  
        
        Console.WriteLine($"It has been {since.Humanize()} since .NET 9 was released.");  
        
  • SDK festlegen

    • Mit der Direktive #:sdk kann der SDK-Typ angegeben werden
      • Beispiel:
        #:sdk Microsoft.NET.Sdk.Web  
        
      • Damit lassen sich auch ASP.NET-Core-Funktionen aktivieren, etwa minimale APIs oder MVC
  • MSBuild-Eigenschaften festlegen

    • Mit #:property lassen sich Build-Eigenschaften direkt setzen
      • Beispiel:
        #:property LangVersion preview  
        
  • shebang-Unterstützung für Shell-Skripte

    • Wenn am Dateianfang #!/usr/bin/dotnet run steht, kann die Datei auf Unix-artigen Systemen direkt als ausführbare Datei verwendet werden
      • Beispiel:
        #!/usr/bin/dotnet run  
        Console.WriteLine("Hello from a C# script!");  
        
      • Nach dem Setzen der Ausführungsrechte direkt starten:
        chmod +x app.cs  
        ./app.cs  
        

Umwandlung in eine projektbasierte App

  • Wenn die App größer wird oder mehr Funktionen benötigt, kann sie mit dem Befehl dotnet project convert app.cs einfach in ein Projekt umgewandelt werden
  • Direktiven werden dabei automatisch in Eigenschaften, Verweise usw. der .csproj-Datei überführt
  • Beispiel für die Umwandlung

    • Eine Datei wie diese:
      #:sdk Microsoft.NET.Sdk.Web  
      #:package Microsoft.AspNetCore.OpenApi@10.*-*  
      
      var builder = WebApplication.CreateBuilder();  
      builder.Services.AddOpenApi();  
      var app = builder.Build();  
      app.MapGet("/", () => "Hello, world!");  
      app.Run();  
      
    • Ergebnis der Umwandlung:
    <Project Sdk="Microsoft.NET.Sdk.Web">  
      <PropertyGroup>  
        <TargetFramework>net10.0</TargetFramework>  
        <ImplicitUsings>enable</ImplicitUsings>  
        <Nullable>enable</Nullable>  
      </PropertyGroup>  
      <ItemGroup>  
        <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.*-*" />  
      </ItemGroup>  
    </Project>  
    

Unterschiede zu bisherigen C#-Skriptansätzen

  • Bisher war die Ausführung von C#-Skripten zwar über Community-Tools wie CS-Script, dotnet-script oder Cake möglich, erforderte aber separate Installation und Konfiguration
  • Jetzt kann Code ohne zusätzliche Installation oder speziellen Modus direkt mit demselben C#-Compiler und derselben Sprache ausgeführt werden, ohne Einstiegshürden

Erste Schritte

  • .NET 10 Preview 4 installieren
  • Bei Nutzung von Visual Studio Code müssen C# Dev Kit und die neueste Prerelease-Version der C#-Erweiterung (2.79.8 oder höher) installiert sein
  • Eine .cs-Datei anlegen und direkt Code schreiben
  • Im Terminal dotnet run hello.cs ausführen
  • Bei Bedarf mit dotnet project convert hello.cs in ein Projekt umwandeln

Mehr erfahren

Nächste Schritte

  • Geplant sind Unterstützung für dateibasierte Apps in VS Code, bessere IntelliSense für Direktiven, Performance-Verbesserungen und stärkere Debugging-Unterstützung
  • Auch zusätzliche Funktionen wie Unterstützung für mehrere Dateien und schnellere Ausführung sind in Entwicklung
  • dotnet run app.cs macht C# leichter zugänglich und liefert gleichzeitig die volle Stärke von .NET
  • Es schafft die Grundlage, um schneller von Prototyping und Schulung zur produktiven Entwicklung zu wechseln

4 Kommentare

 
rkttu 2025-08-18

Die DX, die eine Autovervollständigungs-Erfahrung auf Basis von File-based App bietet, ist zwar in der neuesten Version der C#-Erweiterung verfügbar, ursprünglich hatte Microsoft die Erweiterung jedoch nicht an anderer Stelle als im VS Code Marketplace veröffentlicht.

Um diese Unannehmlichkeit zu beseitigen, habe ich nur den C#-Extension-Teil (den Teil unter MIT-Lizenz) statt des C# Dev Kit so eingerichtet, dass er separat automatisch gebaut/veröffentlicht wird, und ihn bei OpenVSX registriert; darauf aufbauend teile ich ein einfaches, Kiro-basiertes Demo-Video.

https://www.youtube.com/watch?v=pIi7CWOPQSA

 
ndrgrd 2025-05-31

Als ich früher die C#-Interactive-Funktion verwendet habe, konnte ich keine lokal nicht installierten Pakete nutzen, aber offenbar wurde das inzwischen verbessert.

 
GN⁺ 2025-05-30
Hacker-News-Kommentare
  • Diese Funktion könnte die Produktivität von .NET-Entwicklern stark beeinflussen, deshalb ist es etwas schade und man fragt sich, warum sie erst jetzt kommt. Und es gibt noch eine Funktion, die ich mir in .NET-Projekten wirklich wünsche: projektbezogen einfach eigene Kommandos definieren zu können, also so etwas wie npm run <command>.
  • Interessant, dass es aktiv dafür beworben wird, das zusammen mit einem Shebang zu nutzen. Dieser Ansatz wirkt ziemlich attraktiv. Go war auch vor der Einführung von Modulen für solche Skripting-Anwendungsfälle gut geeignet, und ich meine, Ubuntu hat das ebenfalls so verwendet. Die Go-Autoren standen der Nutzung von Go als solche Skriptsprache allerdings eher ablehnend gegenüber.
    • Nicht so sehr, dass die Go-Autoren dagegen gewesen wären, sondern eher, dass sie empfohlen haben, Go in erster Linie als Programmiersprache zu verwenden. Mit Tools wie gorun (https://github.com/erning/gorun) konnte man Go schon lange einfach skriptartig verwenden. Inzwischen wird auch unterstützt, etwas direkt mit einem Tag in einem Schritt auszuführen, etwa go run github.com/kardianos/json/cmd/jsondiff@v1.0.1 — ziemlich cool.
    • Ich frage mich, seit wann und woher der Begriff „shebang“ kommt. Während meiner Studienzeit und im Süden in den 90ern bis frühen 2000ern sagte man meistens „hashbang“. „Shebang“ habe ich zum ersten Mal gehört, als C# populär wurde, obwohl der Begriff tatsächlich schon viel älter ist — nur in meinem Umfeld hatte ich ihn nie gehört.
    • Als ich früher bei einer .NET-Firma gearbeitet habe, hat plötzlich jemand Automatisierungsskripte in bash geschrieben. Es gab dort keine Fachkenntnis, um solche Skripte langfristig zu pflegen, und die Qualität war schon beim ersten Einsatz nicht besonders gut. Ich habe nie verstanden, warum man das Tool nicht einfach direkt in C# geschrieben hat. Mit dieser Funktion könnte ein C#-Ansatz deutlich praktikabler als echte Alternative wirken.
    • Mit Rusts cargo geht so etwas ebenfalls, auch wenn es noch nicht offiziell unterstützt wird: https://rust-lang.github.io/rfcs/3424-cargo-script.html
  • Die Funktion selbst ist großartig, aber selbst im kompilierten Zustand liegt der Startup-Overhead bei etwa 0,5 Sekunden. Dadurch ist sie für viele Anwendungen nicht ideal. Gleichzeitig gibt es natürlich die Grenzen von Shell-Skripting, das auf bash basiert, Perl ist längst vorbei, und Ruby ist für solche Zwecke immer noch hervorragend, weshalb ich es weiter benutzt habe. In letzter Zeit habe ich einige Skripte nach Swift portiert; das ist standardmäßig interpreterbasiert und dadurch viel schneller, und kompilierte ausführbare Dateien starten sofort, was sehr beeindruckend ist. Ich habe sogar selbst einen Caching-Compiler für Swift-CLI-Apps gebaut (https://github.com/jrz/tools). Zur Einordnung: dotnet run cached die Kompilierung bereits, daher braucht man keine separate Caching-Schicht (zum Deaktivieren --no-build, für den Binärpfad --artifacts-path).
    • Woher stammt eigentlich die Zahl von 0,5 Sekunden? Ich habe mit Hello World getestet und kam auf 63 ms. Auch in neueccs Benchmark der CLI-Bibliothek (https://neuecc.medium.com/consoleappframework-v5-zero-overhead-native-aot-compatible-cli-framework-for-c-8f496df8d9d1) erreicht nichts 0,5 Sekunden. Und du hattest erwähnt, dass Swift standardmäßig interpreterbasiert ist; .NET JIT ist dagegen tiered JIT, also eine Struktur mit mehreren Stufen, bei der der Code nicht sofort vollständig erzeugt wird.
    • Ich habe gehört, dass in dotnet Version 10 oder 11 ein vollständiger Interpreted Mode eingeführt werden soll. Ich frage mich, ob dieser Modus auch für solche Anwendungsfälle genutzt werden kann: https://github.com/dotnet/runtime/issues/112748
    • Wenn es selbst kompiliert noch etwas Startverzögerung gibt, warum ist dann ausgerechnet Python in diesem Bereich so populär geworden?
    • Diese Funktion ist noch in einer frühen Preview-Phase. In mehreren Präsentationen wurde gesagt, dass das Startup-Tempo als Problem bekannt ist und verbessert wird.
    • Wenn man schnellen Start will, kann man es gemäß https://learn.microsoft.com/en-us/dotnet/core/deploying/ leicht in nativen Code umwandeln.
  • Schade, dass CSX/VBX-Projekte kaum erwähnt werden: https://ttu.github.io/dotnet-script/. Es ist auch interessant, dass man sich offenbar für einen Ansatz entschieden hat, der mit der Abhängigkeitsbehandlung von F#-Skripten in der C#-Runtime nicht kompatibel ist: https://learn.microsoft.com/en-us/dotnet/fsharp/tools/fsharp-interactive/
    • Zu der Aussage, Bemühungen wie CSX/VBX seien nicht eingeflossen: Tatsächlich werden offiziell verschiedene Wege und Tools erwähnt: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#existing-ways-to-run-c#-without-projects
    • Was genau ist mit „inkompatibel zu F#“ gemeint? Falls es um Syntaxunterschiede geht: Es gab einen bewussten Grund, die Syntax anders zu gestalten, und man wollte keinen neuen C#-Skript-Dialekt schaffen, weshalb Funktionen wie Datei-Import absichtlich blockiert wurden. Das hängt mit den Eigenschaften von C# zusammen.
  • In Kotlin gibt es etwas Ähnliches: https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md (hier funktioniert es nur, wenn die Dateiendung zwingend *.main.kts ist). Solche Ansätze sind großartig für kleine Skripte oder Prototyping und auch praktisch, um JVM-Funktionen zu nutzen. Für kleine Skripte bleibt Ruby aber weiterhin am bequemsten, besonders wenn man externe Programme ausführt — die Backtick-Syntax ist dafür wirklich praktisch.
  • Mit Shebang kann man C#-Skripte wie bash-Skripte ausführen: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts
    • Ich habe den Shebang im .net10 preview 4-SDK-Image getestet, um Dateien direkt auszuführen, und zunächst funktionierte es nicht. Mit dotnet run <file> lief es aber. Nach einem Update funktionierte es korrekt; die Ursache war, dass die Datei CRLF statt LF als Zeilenende verwendet hatte.
    • Ich freue mich sehr, dass man jetzt Skripte mit Type Safety schreiben kann. Unter macOS muss man im Shebang übrigens #!/usr/local/share/dotnet/dotnet run oder #!/usr/bin/env -S dotnet run verwenden.
  • Das wirkt wie ein möglicher Ersatz für PowerShell. PowerShell wird inzwischen fast wie eine reine ChatGPT-Sprache verwendet. In den meisten Unternehmen übernehmen PowerShell-Skripte zwar eine Schlüsselrolle in der Infrastruktur, geraten aber faktisch oft in einen Zustand von „nur noch lesen“.
    • Es fühlt sich so an, als könne es nicht nur PowerShell, sondern einen viel größeren Bereich ersetzen. Wenn man ohnehin ein .NET-Team ist, müsste man weder Python noch Shell-Skripte anfassen: Shebang oben rein, ein C#-Snippet einkleben, und fast jedes Skript ließe sich damit erledigen. Selbst einen Testservice müsste man nicht extra in express.js bauen; eine ASP.NET Minimal API reicht völlig.
    • Windows-Systemadministratoren sind wahrscheinlich die Gruppe mit den meisten ChatGPT-Skripten überhaupt. Wäre ich noch Administrator, hätte ich es bei dem Stand der offiziellen MS-Dokumentation sicher ausprobiert.
    • Man kann auch C#-Code aus PowerShell aufrufen: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.5
    • Die gesamte Infrastruktur in PowerShell zu skripten ist in der Praxis gar nicht so einfach, und wenn man es trotzdem tut, entsteht nur Chaos. Sobald es über ein paar Funktionen hinausgeht, ist C# deutlich effizienter und die Einstiegshürde fast null. PowerShell ist optimal für kleine Ad-hoc-Skripte und nimmt heute ungefähr die Rolle ein, die früher VBScript als „eingebaute Windows-Skriptsprache“ hatte.
    • Da PowerShell .NET-Code direkt ausführen kann, erweitert sich dadurch gewissermaßen sogar die PowerShell-Erfahrung.
  • Es wirkt fast wie ein faktischer Ersatz für NetPad, und wenn noch Debugging dazukommt, könnte sogar LINQPad an Relevanz verlieren. Ich habe früher stark von LINQPad profitiert, aber die Texteditor-Erfahrung ist heute immer noch zu unbequem. Für ernsthaftes Schreiben oder Bearbeiten von Code gibt es klare Grenzen.
    • Für mich liegt der Hauptnutzen von LINQPad in der Interaktion mit Datenbanken oder darin, Werte mit .dump() zu erkunden. Dieses dotnet run könnte dafür eher eine Ergänzung sein. Früher habe ich an einem Ort gearbeitet, an dem man PowerShell extrem hasste, und dort wurde fast das gesamte Skripting mit LINQPad erledigt — in so einem Umfeld war es brauchbar.
    • LINQPad ist in .NET ein einzigartiges Produkt, aber der Texteditor gehört oft fast zum Schlechtesten, was es je gab. Es wäre schön, wenn er durch etwas wie neovim oder monaco ersetzt würde. Tabellenvisualisierung und Snappiness sind großartig, aber verglichen mit heutigen „Notebook“-Technologien wie Jupyter Notebook ist der Einsatzbereich enger. Wahrscheinlich liegt das auch daran, dass es von einem Einzelentwickler stammt. Trotzdem ist LINQPad im Arbeitsalltag beim Umgang mit SQL-Daten für mich weiterhin unübertroffen.
    • LINQPad wird wahrscheinlich nicht sofort ersetzt. Die Hälfte seiner Stärke liegt meiner Meinung nach in der UI. Ich frage mich, wie ähnlich sich die Nutzung von dotnet run in VSCode oder Visual Studio im Vergleich zu LINQPad anfühlt. Eine Stärke von LINQPad ist die Visualisierung von Ergebnissen. Wenn dotnet run nur Text ausgibt oder viele zusätzliche Plugins braucht, wird es weiter Bedarf für LINQPad geben. Wenn man nur kurz Syntax prüfen will, könnte dotnet run die bessere Wahl sein. Ich selbst probiere gelegentlich verwirrende Syntax auch in LINQPad aus.
    • Ohne die GUI-Funktionen und Erweiterungspunkte vollständig nachzubauen, wird LINQPad kaum direkt zu ersetzen sein.
  • Ich freue mich wirklich auf diese Funktion. Sie könnte einige der PowerShell-Skripte ersetzen, die ich in CI/CD-Pipelines verwende. Ich mag sowohl PowerShell als auch Bash, aber es gibt definitiv Aufgaben, die sich im Kopf viel effizienter in einer Sprache mit C-artiger Syntax lösen lassen. Diese Funktion könnte genau diese Lücke schließen.
  • Im eigentlichen Vorschlag (https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md) stehen noch mehr Informationen, besonders zur Verarbeitung mehrerer Dateien und zu Implementierungsdetails wie impliziten Projektdateien.
 
rkttu 2025-05-30

Ich habe dazu auch zwei praktische Beispiele erstellt und teile sie hier als Antwort. Es handelt sich um Beispielcode für GUI-Apps unter Windows und macOS mit einem MCP-Server und Avalonia. 😊

https://forum.dotnetdev.kr/t/…