4 puntos por GN⁺ 2025-05-30 | 4 comentarios | Compartir por WhatsApp
  • Desde .NET 10 Preview 4, ahora se agregó una función que permite ejecutar un solo archivo C# directamente con dotnet run app.cs, haciendo posible ejecutar código C# incluso sin archivo de proyecto
  • Gracias a las aplicaciones basadas en archivos (file-based apps), ahora es mucho más fácil ejecutar scripts simples, hacer pruebas y experimentar ideas, como en Python o JavaScript
  • También se pueden gestionar dentro del archivo, mediante directivas, cosas como referencias a paquetes NuGet, definición del SDK y configuración de propiedades de compilación, lo que mejora la flexibilidad de desarrollo
  • Con soporte para shebang, también se puede usar en sistemas tipo Unix para utilidades CLI, scripts de automatización y más
  • Si hace falta, se puede convertir sin fricción a una aplicación basada en proyecto, conectando de forma natural desde aprendizaje y prototipos hasta desarrollo formal de aplicaciones

Qué es dotnet run app.cs

  • Antes, para ejecutar código C# con la CLI de dotnet, era obligatorio tener una estructura de proyecto (.csproj)
  • Ahora se puede ejecutar directamente con un solo archivo .cs, lo que reduce mucho la barrera de entrada
  • Es adecuado para varios usos, como lenguajes de scripting, automatización, experimentación y aprendizaje
  • Gracias a la integración con la CLI, se puede usar de inmediato con solo tener dotnet, sin instalar herramientas adicionales
  • Si el código crece, se puede ampliar a una app basada en proyecto usando el mismo lenguaje y las mismas herramientas

Soporte para directivas a nivel de archivo

  • En las apps basadas en archivos también se pueden declarar directamente en el archivo .cs las configuraciones principales del proyecto mediante directivas
  • Referencia a paquetes NuGet

    • Con la directiva #:package se pueden referenciar directamente paquetes NuGet
      • Ejemplo:
        #: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.");  
        
  • Definición del SDK

    • Con la directiva #:sdk se puede especificar el tipo de SDK
      • Ejemplo:
        #:sdk Microsoft.NET.Sdk.Web  
        
      • También se pueden habilitar funciones de ASP.NET Core (Minimal API, MVC, etc.)
  • Configuración de propiedades de MSBuild

    • Con #:property se pueden definir directamente propiedades de compilación
      • Ejemplo:
        #:property LangVersion preview  
        
  • Soporte de shebang para scripts de shell

    • Si se agrega #!/usr/bin/dotnet run al inicio del archivo, se puede usar directamente como ejecutable en sistemas tipo Unix
      • Ejemplo:
        #!/usr/bin/dotnet run  
        Console.WriteLine("Hello from a C# script!");  
        
      • Tras dar permisos de ejecución, se puede ejecutar directamente:
        chmod +x app.cs  
        ./app.cs  
        

Conversión a una app basada en proyecto

  • Cuando la app crece o se necesitan más funciones, se puede convertir fácilmente a un proyecto con el comando dotnet project convert app.cs
  • Las directivas se convierten automáticamente en propiedades del archivo .csproj, referencias, etc.
  • Ejemplo de conversión

    • Un archivo como este:
      #: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();  
      
    • Resultado de la conversión:
    <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>  
    

Diferencias frente al método tradicional de scripts en C#

  • Hasta ahora era posible ejecutar scripts C# con herramientas de la comunidad como CS-Script, dotnet-script y Cake, pero requerían instalación y configuración por separado
  • Ahora, sin instalación adicional ni modos especiales, se puede ejecutar código de inmediato usando el mismo compilador y el mismo lenguaje de C#, reduciendo la barrera de entrada

Cómo empezar

  • Instalar .NET 10 Preview 4
  • Si usas Visual Studio Code, necesitas instalar C# Dev Kit y la versión prerelease más reciente de la extensión C# (2.79.8 o superior)
  • Crear un archivo .cs y escribir el código directamente
  • Ejecutar dotnet run hello.cs en la terminal
  • Si hace falta, convertirlo en proyecto con dotnet project convert hello.cs

Más información

Próximos planes

  • Está previsto mejorar el soporte para apps basadas en archivos dentro de VS Code y el IntelliSense para directivas, además de mejorar el rendimiento y reforzar el soporte de depuración
  • También se están desarrollando funciones adicionales, como soporte para múltiples archivos y mejoras en la velocidad de ejecución
  • dotnet run app.cs hace que C# sea más accesible, manteniendo intacta toda la potencia de .NET
  • Proporciona una base para pasar más rápido del prototipado y la enseñanza al desarrollo en producción

4 comentarios

 
rkttu 2025-08-18

La experiencia de DX que ofrece autocompletado basado en File-based App ya está disponible en la versión más reciente de la extensión de C#, pero originalmente Microsoft no publicaba la extensión en ningún lugar fuera de VS Code Marketplace.

Para resolver esa incomodidad, hice que solo la parte de C# Extension —no C# Dev Kit— (la parte con licencia MIT) se autobuildee y autopublique por separado, la registré en OpenVSX y comparto un video de demostración simple basado en Kiro.

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

 
ndrgrd 2025-05-31

Cuando antes usaba la función de C# Interactive, no se podían usar paquetes que no estuvieran instalados localmente, pero parece que ahora ya lo mejoraron.

 
GN⁺ 2025-05-30
Comentarios en Hacker News
  • Esta función parece de esas que podrían tener un gran impacto en la productividad de los desarrolladores de .NET, así que da un poco de pena que haya llegado recién ahora; y hay otra cosa que realmente me gustaría ver en los proyectos .NET: una forma sencilla de definir comandos personalizados por proyecto, algo tipo npm run <command>
  • Me parece interesante que lo estén promoviendo activamente para usarse junto con shebang; ese enfoque se siente bastante atractivo. Go también servía bien para este tipo de scripting antes de que introdujeran módulos, y según recuerdo Ubuntu también llegó a usarse de esta forma, aunque los autores de Go eran reacios a que se usara como lenguaje de scripting
    • No es que los autores de Go se opusieran, sino que recomendaban usar Go ante todo como lenguaje de programación; desde hace tiempo se podía usar Go como si fuera un script fácilmente con herramientas como gorun (https://github.com/erning/gorun), y más recientemente se agregó soporte para ejecutar algo directamente tomando la etiqueta, por ejemplo go run github.com/kardianos/json/cmd/jsondiff@v1.0.1; es una función bastante genial
    • Me da curiosidad de dónde sale y desde cuándo se usa la palabra “shebang”; cuando iba a la universidad y en el sur a fines de los 90 y principios de los 2000, normalmente le decían hashbang. Yo escuché “shebang” por primera vez cuando C# se puso de moda, aunque el término en realidad existía desde antes; simplemente nunca lo había oído en mi entorno
    • Cuando trabajaba en una empresa .NET, de repente había gente que escribía scripts de automatización en bash. No había experiencia real para mantener esos scripts a largo plazo y la calidad ya era mala desde el inicio. Nunca entendí por qué no simplemente hacían la herramienta en C#. Con esta función, ese enfoque en C# podría empezar a sentirse como una alternativa mucho más realista
    • Con cargo de Rust también se puede hacer algo así, aunque todavía no tiene soporte oficial https://rust-lang.github.io/rfcs/3424-cargo-script.html
  • La función en sí es excelente, pero incluso ya compilado sigue teniendo un overhead de arranque de alrededor de 0.5 segundos, así que en muchas aplicaciones eso lo hace poco adecuado. Aun así, también están las limitaciones del shell scripting basado en bash; la era de perl ya pasó, y Ruby seguía siendo de lo mejor para este tipo de uso, por eso lo seguí usando. Últimamente he migrado algunos scripts a Swift, que por defecto es interpretado y por eso arranca mucho más rápido, y cuando ya tienes un ejecutable compilado este corre de inmediato, lo cual impresiona bastante. Incluso hice mi propio compilador con caché para apps CLI en Swift (https://github.com/jrz/tools). Por cierto, dotnet run ya cachea el resultado de compilación, así que no hace falta una capa de caché aparte (para desactivarlo, --no-build; para ver la ruta del binario, usa --artifacts-path)
  • Me decepciona un poco que casi no haya mención de los proyectos CSX/VBX https://ttu.github.io/dotnet-script/; también me llama la atención que pareciera haberse decidido de una forma que no es compatible con cómo F# script maneja dependencias dentro del runtime de C# https://learn.microsoft.com/en-us/dotnet/fsharp/tools/fsharp-interactive/
    • Sobre el punto de que no se reflejaron esfuerzos como CSX/VBX, se señala que oficialmente sí se mencionan varios enfoques y herramientas https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#existing-ways-to-run-c#-without-projects
    • Preguntan a qué se refiere exactamente con que no es compatible con F#; si habla de diferencias de sintaxis, hubo una intención deliberada de hacer la sintaxis distinta y no querían crear un nuevo dialecto de scripting para C#, por eso bloquearon a propósito cosas como importar archivos; eso tiene que ver con características propias de C#
  • En Kotlin también existe algo parecido, https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md (aquí el archivo tiene que tener obligatoriamente la extensión *.main.kts para que funcione). Este enfoque es muy bueno para scripts pequeños o para prototipado, y también es práctico para aprovechar funciones de la JVM. Aun así, para scripts pequeños Ruby sigue siendo lo más cómodo, especialmente al ejecutar programas externos, porque la sintaxis de backticks es muy práctica
  • Usando shebang se pueden ejecutar scripts de C# como si fueran scripts de bash https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts
    • En la imagen SDK de .net10 preview 4 probé el shebang para ejecutar el archivo directamente, pero al principio no me funcionó. Con dotnet run <file> sí funcionaba; después de actualizar ya quedó bien. El problema era que el archivo usaba saltos de línea CRLF en vez de LF
    • Me alegra muchísimo poder escribir scripts con type safety ahora; por cierto, en macOS hay que usar #!/usr/local/share/dotnet/dotnet run o #!/usr/bin/env -S dotnet run en el shebang
  • Esto se ve como una posible alternativa a PowerShell. PowerShell casi se está usando como si fuera un lenguaje exclusivo de ChatGPT; en la mayoría de las empresas, los scripts hechos en PowerShell cumplen un rol central en la infraestructura, pero muchas veces terminan prácticamente en estado de “solo lectura”
    • Da la sensación de que tiene potencial para reemplazar no solo PowerShell sino un espectro mucho más amplio. Si tu equipo ya trabaja con .NET, quizá ya no haría falta tocar Python ni scripts de shell: bastaría con poner un shebang arriba y pegar un snippet en C# para resolver casi cualquier tarea de scripting. Incluso para servicios de prueba no haría falta armarlos en express.js; con ASP.NET minimal API lo haces rapidísimo
    • Los administradores de sistemas en Windows probablemente sean el grupo que más scripts de ChatGPT usa a gran escala; si yo siguiera siendo admin, viendo el nivel de la documentación oficial de Microsoft, seguro lo habría usado sí o sí
    • También se puede invocar código C# desde PowerShell https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.5
    • En la práctica no es tan fácil meter toda la infraestructura en scripts de PowerShell, y hacerlo solo genera un caos total. En cuanto pasas de unas pocas funciones, C# se vuelve mucho más eficiente y la barrera de entrada casi no existe. PowerShell es ideal para scripts ad-hoc pequeños, y ocupa el lugar de “lenguaje de scripting por defecto de Windows” que antes tenía VBScript
    • Como PowerShell puede ejecutar código .NET directamente, en cierto sentido esto incluso amplía la experiencia de PowerShell
  • Esto parece reemplazar de hecho varias funciones de NetPad y, si le agregan depuración, tengo la impresión de que hasta LINQPad podría empezar a perder terreno. Yo también le saqué muchísimo provecho a LINQPad en su momento, pero la experiencia de editor de texto sigue siendo demasiado incómoda para los tiempos actuales; tiene límites claros como herramienta para escribir o editar código de verdad
    • En mi caso, el uso principal de LINQPad es interactuar con bases de datos o explorar valores con .dump(). Este dotnet run más bien parece una herramienta que podría complementarlo. Antes trabajé en lugares donde odiaban PowerShell con pasión, y allí resolvían casi todo el scripting con LINQPad; en ambientes así sí resultaba útil
    • LINQPad es un producto único dentro del ecosistema .NET, pero su editor de texto muchas veces se siente de lo peor que ha habido. Ojalá lo cambiaran por algo como neovim o monaco. La visualización de tablas y la rapidez están muy bien, pero comparado con tecnologías actuales de “notebooks” como Jupyter Notebook, el rango de uso se queda más corto. Supongo que también influye que sea un desarrollo de una sola persona. Aun así, para trabajar a diario con datos SQL, LINQPad sigue siendo excelente
    • No creo que LINQPad vaya a ser reemplazado de inmediato; siento que la mitad de su valor está en la UI. Me pregunto qué tan parecida será la experiencia de usar dotnet run desde VSCode o Visual Studio comparada con LINQPad. La gran fortaleza de LINQPad es la visualización de resultados; si dotnet run solo imprime texto o requiere muchos plugins aparte, LINQPad seguirá teniendo demanda. Para cosas como revisar sintaxis, eso sí, dotnet run podría ser una mejor opción; yo también a veces pruebo sintaxis en LINQPad cuando se me cruzan los cables
    • A menos que implementen todas las funciones de GUI y los puntos de extensión, va a ser difícil que reemplace a LINQPad de inmediato
  • De verdad me entusiasma esta función; creo que podría reemplazar parte de los scripts de PowerShell que uso en pipelines de CI/CD. Me gustan tanto PowerShell como Bash, pero hay tareas que simplemente se resuelven mucho mejor mentalmente en un lenguaje de sintaxis tipo C, y esta función podría llenar justo ese hueco
  • En la propuesta real (https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md) hay mucha más información, especialmente sobre manejo de múltiples archivos y detalles de implementación (como archivos de proyecto implícitos)
 
rkttu 2025-05-30

También hice dos ejemplos reales relacionados con esta función, así que los comparto en una respuesta. Son códigos de ejemplo de apps GUI para Windows y macOS usando un servidor MCP y Avalonia. 😊

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