4 points par GN⁺ 2025-05-30 | 4 commentaires | Partager sur WhatsApp
  • À partir de .NET 10 Preview 4, une nouvelle fonctionnalité permet désormais d’exécuter directement un fichier C# unique avec dotnet run app.cs, ce qui rend possible l’exécution de code C# sans fichier de projet
  • Grâce aux applications basées sur un fichier (file-based apps), il devient beaucoup plus simple d’exécuter de petits scripts, de faire des tests et d’expérimenter des idées, comme en Python ou en JavaScript
  • Les références de packages NuGet, le choix du SDK et les propriétés de build peuvent aussi être gérés via des directives dans le fichier, ce qui améliore la flexibilité de développement
  • La prise en charge de shebang permet aussi un usage sur les systèmes Unix pour des utilitaires CLI, des scripts d’automatisation, etc.
  • Si nécessaire, il est possible de convertir facilement le fichier en application basée sur un projet, assurant une transition naturelle entre apprentissage, prototypage et développement d’applications à plus grande échelle

Qu’est-ce que dotnet run app.cs ?

  • Jusqu’ici, pour exécuter du code C# avec la CLI dotnet, il fallait obligatoirement une structure de projet (.csproj)
  • Désormais, un simple fichier .cs suffit pour exécuter directement le code, ce qui réduit fortement la barrière à l’entrée
  • Cette approche convient à de nombreux usages, comme les langages de script, l’automatisation, l’expérimentation ou l’apprentissage
  • Grâce à l’intégration à la CLI, aucun outil supplémentaire n’est nécessaire : dotnet suffit
  • Si le code prend de l’ampleur, il peut être étendu en application basée sur un projet avec le même langage et les mêmes outils

Prise en charge des directives au niveau du fichier

  • Même avec une application basée sur un fichier, il est possible de déclarer directement dans le fichier .cs les principaux réglages habituellement définis dans un projet
  • Référence à des packages NuGet

    • La directive #:package permet de référencer directement des packages NuGet
      • Exemple :
        #: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.");  
        
  • Choix du SDK

    • La directive #:sdk permet de spécifier le type de SDK
      • Exemple :
        #:sdk Microsoft.NET.Sdk.Web  
        
      • Elle permet aussi d’activer des fonctionnalités ASP.NET Core (minimal API, MVC, etc.)
  • Définition de propriétés MSBuild

    • #:property permet de définir directement des propriétés de build
      • Exemple :
        #:property LangVersion preview  
        
  • Prise en charge de shebang pour les scripts shell

    • En ajoutant #!/usr/bin/dotnet run tout en haut du fichier, il peut être utilisé directement comme fichier exécutable sur les systèmes Unix
      • Exemple :
        #!/usr/bin/dotnet run  
        Console.WriteLine("Hello from a C# script!");  
        
      • Après avoir accordé les droits d’exécution, il peut être lancé directement :
        chmod +x app.cs  
        ./app.cs  
        

Conversion vers une application basée sur un projet

  • Lorsque l’application grandit ou a besoin de davantage de fonctionnalités, la commande dotnet project convert app.cs permet une conversion simple vers un projet
  • Les directives sont automatiquement converties en propriétés de fichier .csproj, références, etc.
  • Exemple de conversion

    • Fichier de départ :
      #: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();  
      
    • Résultat de la conversion :
    <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>  
    

Différences avec les méthodes existantes de script C#

  • Jusqu’à présent, l’exécution de scripts C# était possible via des outils communautaires (CS-Script, dotnet-script, Cake, etc.), mais cela nécessitait l’installation et la configuration d’outils séparés
  • Désormais, il est possible d’exécuter du code immédiatement, sans installation additionnelle ni mode spécifique, avec le même compilateur C# et le même langage

Comment démarrer

  • Installer .NET 10 Preview 4
  • Si vous utilisez Visual Studio Code, il faut installer C# Dev Kit ainsi que la dernière version pre-release de l’extension C# (2.79.8 ou supérieure)
  • Créer un fichier .cs, puis écrire le code directement
  • Exécuter dotnet run hello.cs dans le terminal
  • Si nécessaire, convertir ensuite en projet avec dotnet project convert hello.cs

Pour en savoir plus

Feuille de route

  • La prise en charge des applications basées sur un fichier dans VS Code, l’amélioration de l’IntelliSense pour les directives, les performances et le débogage sont prévus
  • D’autres fonctionnalités sont également en cours de développement, comme la prise en charge de plusieurs fichiers et l’amélioration de la vitesse d’exécution
  • dotnet run app.cs rend C# plus accessible tout en conservant toute la puissance de .NET
  • Il fournit une base permettant de passer plus rapidement du prototypage, de la formation et de l’expérimentation au développement en production

4 commentaires

 
rkttu 2025-08-18

L’expérience DX proposant l’autocomplétion basée sur les File-based Apps est disponible dans la dernière version de l’extension C#, mais à l’origine Microsoft ne publiait pas l’extension ailleurs que sur la VS Code Marketplace.

Pour résoudre cet inconvénient, seule la partie C# Extension du C# Dev Kit (la partie sous licence MIT) a été configurée pour être autobuild/auto-publish séparément puis enregistrée sur OpenVSX, et je partage une courte vidéo de démonstration basée sur Kiro à partir de cela.

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

 
ndrgrd 2025-05-31

Quand j’avais utilisé la fonctionnalité C# Interactive auparavant, je ne pouvais pas utiliser les packages qui n’étaient pas installés en local, mais apparemment cela a été amélioré maintenant.

 
GN⁺ 2025-05-30
Avis Hacker News
  • Cette fonctionnalité semble pouvoir avoir un gros impact sur la productivité des développeurs .NET, ce qui fait aussi se demander avec un peu de regret pourquoi elle n’arrive que maintenant. Et il y a une fonctionnalité que je voudrais vraiment dans les projets .NET : pouvoir définir facilement des commandes personnalisées par projet, dans le style de "npm run <command>".
  • C’est intéressant de voir qu’ils en font activement la promotion avec le shebang ; cette approche est assez séduisante. Go était aussi pratique pour ce genre d’usages de scripting avant même l’introduction des modules, et il me semble qu’Ubuntu l’a utilisé de cette manière, mais les auteurs de Go étaient opposés à l’idée d’utiliser Go comme langage de script.
    • Ce n’est pas que les auteurs de Go y étaient opposés, mais plutôt qu’ils recommandaient d’utiliser Go d’abord comme langage de programmation. Depuis longtemps, on pouvait déjà utiliser facilement Go comme un script avec des outils comme gorun (https://github.com/erning/gorun) ; plus récemment, il est aussi possible d’exécuter directement quelque chose comme go run github.com/kardianos/json/cmd/jsondiff@v1.0.1 en acceptant le tag dans la commande, ce qui est plutôt élégant.
    • Je me demande d’où vient le terme « shebang » et à partir de quand il s’est imposé ; à l’université et dans le sud des États-Unis entre les années 90 et le début des années 2000, on disait plutôt hashbang. Je n’ai entendu « shebang » pour la première fois qu’à l’époque où C# est devenu populaire, alors que le terme existait déjà bien avant ; c’était juste un mot qu’on n’entendait pas autour de moi.
    • Quand je travaillais autrefois dans une entreprise .NET, quelqu’un se mettait parfois soudainement à écrire des scripts d’automatisation en bash. Personne n’avait l’expertise pour maintenir ça dans la durée, et la qualité n’était déjà pas bonne au départ. Je ne comprenais pas pourquoi on ne faisait pas simplement l’outil en C#. Avec cette fonctionnalité, l’approche C# pourrait enfin sembler beaucoup plus réaliste comme alternative.
    • C’est aussi possible avec cargo en Rust, même si ce n’est pas encore officiellement pris en charge https://rust-lang.github.io/rfcs/3424-cargo-script.html.
  • La fonctionnalité elle-même est excellente, mais même compilée, elle a un overhead de démarrage d’environ 0,5 seconde, ce qui la rend inadaptée à beaucoup d’applications. Cela dit, le scripting shell basé sur bash a aussi ses limites, l’époque de perl est passée, et Ruby reste excellent pour ce genre d’usage, raison pour laquelle je continue à l’utiliser. Récemment, j’ai migré quelques scripts vers Swift ; comme c’est fondamentalement interprété, c’est bien plus rapide, et les exécutables compilés démarrent immédiatement, ce qui est très impressionnant. J’ai même créé mon propre compilateur avec cache pour les applications CLI Swift (https://github.com/jrz/tools). À noter que dotnet run met déjà en cache le résultat de compilation, donc il n’y a pas besoin d’une couche de cache séparée (pour la désactiver : --no-build, pour voir le chemin du binaire : --artifacts-path).
    • Je me demande d’où vient ce chiffre de 0,5 seconde ; j’ai testé avec hello world et j’ai obtenu 63 ms. Dans le benchmark de la bibliothèque CLI de neuecc (https://neuecc.medium.com/consoleappframework-v5-zero-overhead-native-aot-compatible-cli-framework-for-c-8f496df8d9d1), rien n’atteint les 0,5 seconde. Et au sujet du fait que Swift serait fondamentalement interprété : le JIT de .NET est un tiered JIT, donc le code n’est pas produit d’un coup mais en plusieurs étapes.
    • J’ai entendu dire que dotnet devrait introduire un mode entièrement interprété en version 10 ou 11, et je me demande si ce mode s’appliquera à ce type d’usage https://github.com/dotnet/runtime/issues/112748.
    • Même en étant compilé, s’il y a toujours un petit lag au démarrage, je me demande alors pourquoi Python est devenu si populaire dans ce domaine.
    • Cette fonctionnalité n’en est encore qu’à un stade de preview très précoce ; dans plusieurs présentations, ils ont indiqué qu’ils avaient identifié le problème de vitesse de démarrage et qu’ils travaillaient à l’améliorer.
    • Si on veut un démarrage rapide, on peut aussi facilement convertir en code natif comme expliqué ici : https://learn.microsoft.com/en-us/dotnet/core/deploying/.
  • C’est un peu dommage qu’il y ait si peu de mention des projets CSX/VBX https://ttu.github.io/dotnet-script/ ; je trouve aussi curieux qu’ils aient choisi une approche qui n’est pas compatible avec la gestion des dépendances des scripts F# dans le runtime C# https://learn.microsoft.com/en-us/dotnet/fsharp/tools/fsharp-interactive/.
    • À propos de l’idée que les efforts comme CSX/VBX n’ont pas été pris en compte, il faut noter que plusieurs approches et outils existants sont bien mentionnés officiellement ici : https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#existing-ways-to-run-c#-without-projects.
    • Quelqu’un demande ce que signifie exactement « incompatible avec F# » ; si c’est une question de différences de syntaxe, il y avait une volonté assumée de les rendre différentes, car ils ne voulaient pas créer un nouveau dialecte de script C#, et c’est pour cela que certaines fonctionnalités comme l’import de fichiers ont été volontairement bloquées ; cela tient à la nature même de C#.
  • Il existe aussi une fonctionnalité similaire en Kotlin, https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md (ici, l’extension du fichier doit obligatoirement être "*.main.kts" pour fonctionner). Ce genre d’approche est excellent pour les petits scripts ou le prototypage, et c’est aussi pratique pour exploiter les capacités de la JVM. Malgré tout, pour les petits scripts, Ruby reste de loin le plus confortable, surtout grâce à la syntaxe avec backticks pour lancer des programmes externes.
  • On peut exécuter des scripts C# comme des scripts bash en utilisant le shebang https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts
    • J’ai testé le shebang pour exécuter directement un fichier dans l’image SDK .net10 preview 4, mais ça ne marchait pas au début. Avec dotnet run <file>, en revanche, ça fonctionnait. Après une mise à jour, tout s’est mis à marcher correctement ; le problème venait du fait que le fichier utilisait des fins de ligne CRLF au lieu de LF.
    • Je suis vraiment ravi qu’on puisse désormais écrire des scripts avec type safety ; sur macOS, il faut utiliser #!/usr/local/share/dotnet/dotnet run ou #!/usr/bin/env -S dotnet run dans le shebang.
  • Ça ressemble à un bon candidat pour remplacer PowerShell. PowerShell a presque tendance à devenir un langage réservé à ChatGPT ; dans beaucoup d’entreprises, les scripts PowerShell jouent un rôle central dans l’infra, mais ils finissent souvent en pratique dans un état « lecture seule ».
    • J’ai l’impression que le potentiel de remplacement va bien au-delà de PowerShell. Pour une équipe .NET, il pourrait devenir inutile de toucher à Python ou aux scripts shell : il suffirait d’ajouter un shebang en haut et de coller un snippet C# pour gérer presque tous les besoins de scripting. Même pour un service de test, pas besoin de bricoler quelque chose en express.js : un ASP.NET minimal API ferait l’affaire en quelques instants.
    • Les administrateurs système Windows sont peut-être le plus gros groupe d’utilisateurs de scripts générés par ChatGPT ; si j’étais encore admin aujourd’hui, vu le niveau de la documentation officielle Microsoft, j’en aurais certainement beaucoup utilisé.
    • Il est aussi possible d’appeler du code C# depuis PowerShell https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.5
    • En pratique, mettre toute l’infrastructure en scripts PowerShell n’est pas vraiment simple, et cela ne mène qu’au chaos. Dès qu’on dépasse quelques fonctions, C# devient bien plus efficace, avec une barrière d’entrée presque inexistante. PowerShell est optimal pour les petits scripts ad hoc, et occupe un peu la place de « langage de script par défaut de Windows » qu’avait autrefois VBScript.
    • Comme PowerShell peut exécuter directement du code .NET, on peut aussi y voir une extension naturelle de l’expérience PowerShell.
  • Cela donne presque l’impression de remplacer NetPad, et si le débogage s’ajoute, LINQPad pourrait aussi perdre sa place. J’ai moi-même beaucoup profité de LINQPad autrefois, mais l’expérience d’éditeur de texte y est encore trop inconfortable pour l’époque actuelle ; pour écrire ou éditer du code sérieusement, on atteint vite ses limites.
    • Dans mon cas, l’usage principal de LINQPad est l’interaction avec la base de données ou l’exploration de valeurs avec .dump(). Ce nouveau dotnet run pourrait plutôt devenir un outil complémentaire. J’ai déjà travaillé dans un endroit qui détestait PowerShell au plus haut point, où presque tout le scripting passait par LINQPad ; dans ce genre de contexte, c’était utile.
    • LINQPad est un produit unique dans l’écosystème .NET, mais son éditeur de texte est souvent proche d’être l’un des pires que j’aie connus. Ce serait bien qu’il passe à quelque chose comme neovim ou monaco. La visualisation tabulaire et la réactivité sont excellentes, mais face aux technologies de type notebook comme Jupyter aujourd’hui, le champ d’usage paraît plus limité ; c’est peut-être aussi lié au fait qu’il s’agit d’un développement solo. Malgré cela, pour manipuler des données SQL au quotidien, LINQPad reste imbattable.
    • LINQPad ne sera probablement pas remplacé immédiatement ; je pense que la moitié de sa valeur vient de son UI. Je me demande à quel point l’expérience avec dotnet run dans VSCode ou Visual Studio pourra se rapprocher de LINQPad. Le gros atout de LINQPad, c’est la visualisation des résultats. Si dotnet run se contente d’afficher du texte ou nécessite beaucoup de plugins, la demande pour LINQPad continuera sans doute. Pour simplement vérifier de la syntaxe, en revanche, dotnet run peut être un meilleur choix ; il m’arrive moi aussi d’utiliser LINQPad pour tester une syntaxe qui me fait hésiter.
    • À moins d’implémenter aussi toutes les fonctionnalités GUI et les points d’extension, il sera difficile de remplacer LINQPad du jour au lendemain.
  • J’attends vraiment cette fonctionnalité avec impatience ; elle pourrait me permettre de remplacer une partie des scripts PowerShell que j’utilise dans mes pipelines CI/CD. J’aime bien PowerShell comme Bash, mais il y a clairement des tâches qu’il est bien plus efficace de raisonner dans un langage à syntaxe de type C, et cette fonctionnalité pourrait combler exactement ce manque.
  • La proposition elle-même (https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md) contient encore plus d’informations, notamment sur la gestion de plusieurs fichiers ainsi que sur les détails d’implémentation, comme les fichiers de projet implicites.
 
rkttu 2025-05-30

J’ai aussi créé deux exemples concrets liés à cette fonctionnalité, que je partage en réponse. Il s’agit d’exemples de code d’applications GUI Windows et macOS utilisant un serveur MCP et Avalonia. 😊

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