4 điểm bởi GN⁺ 2025-05-30 | 4 bình luận | Chia sẻ qua WhatsApp
  • Từ .NET 10 Preview 4, đã có thêm khả năng chạy trực tiếp một tệp C# đơn lẻ bằng dotnet run app.cs, giúp có thể thực thi mã C# ngay cả khi không có tệp dự án
  • Nhờ file-based apps, việc chạy script đơn giản, kiểm thử và thử nghiệm ý tưởng trở nên dễ dàng hơn nhiều, tương tự như Python hay JavaScript
  • Các thiết lập như tham chiếu gói NuGet, chỉ định SDK, cấu hình thuộc tính build cũng có thể được quản lý bằng directive ngay trong tệp, tăng tính linh hoạt cho phát triển
  • Hỗ trợ shebang giúp có thể dùng cho tiện ích CLI, script tự động hóa trên hệ Unix
  • Khi cần, có thể chuyển mượt sang ứng dụng dựa trên project, giúp kết nối tự nhiên từ học tập và tạo prototype đến phát triển ứng dụng thực thụ

dotnet run app.cs là gì

  • Trước đây, để chạy mã C# bằng dotnet CLI thì bắt buộc phải có cấu trúc project (.csproj)
  • Giờ đây có thể chạy ngay chỉ với một tệp .cs duy nhất, giúp hạ thấp đáng kể rào cản tiếp cận
  • Phù hợp cho nhiều mục đích như ngôn ngữ script, tự động hóa, thử nghiệm và học tập
  • Nhờ tích hợp với CLI, chỉ cần có dotnet là dùng được ngay mà không cần cài thêm công cụ
  • Khi mã nguồn lớn dần, có thể mở rộng thành ứng dụng dựa trên project bằng cùng một ngôn ngữ và bộ công cụ

Hỗ trợ directive ở cấp độ tệp

  • Ngay cả với file-based app, các thiết lập quan trọng của project cũng có thể được khai báo trực tiếp bằng directive trong tệp .cs
  • Tham chiếu gói NuGet

    • Có thể tham chiếu trực tiếp gói NuGet bằng directive #:package
      • Ví dụ:
        #: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.");  
        
  • Chỉ định SDK

    • Có thể chỉ định loại SDK bằng directive #:sdk
      • Ví dụ:
        #:sdk Microsoft.NET.Sdk.Web  
        
      • Cũng có thể kích hoạt các tính năng ASP.NET Core (minimal API, MVC, v.v.)
  • Thiết lập thuộc tính MSBuild

    • Có thể chỉ định trực tiếp thuộc tính build bằng #:property
      • Ví dụ:
        #:property LangVersion preview  
        
  • Hỗ trợ shebang cho shell script

    • Thêm #!/usr/bin/dotnet run ở đầu tệp để dùng trực tiếp như tệp thực thi trên hệ Unix
      • Ví dụ:
        #!/usr/bin/dotnet run  
        Console.WriteLine("Hello from a C# script!");  
        
      • Cấp quyền thực thi rồi chạy ngay:
        chmod +x app.cs  
        ./app.cs  
        

Chuyển đổi sang ứng dụng dựa trên project

  • Khi ứng dụng lớn hơn hoặc cần thêm tính năng, có thể chuyển sang project dễ dàng bằng lệnh dotnet project convert app.cs
  • Các directive sẽ được tự động chuyển thành thuộc tính, tham chiếu... trong tệp .csproj
  • Ví dụ chuyển đổi

    • Tệp như bên dưới:
      #: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();  
      
    • Kết quả chuyển đổi:
    <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>  
    

Khác biệt với các cách dùng C# script trước đây

  • Trước giờ vẫn có thể chạy C# script bằng các công cụ cộng đồng như CS-Script, dotnet-script, Cake, nhưng cần cài đặt và cấu hình công cụ riêng
  • Giờ đây, có thể chạy mã ngay không rào cản bằng chính cùng compiler và ngôn ngữ C#, không cần cài đặt hay chế độ riêng biệt

Cách bắt đầu

  • Cài đặt .NET 10 Preview 4
  • Nếu dùng Visual Studio Code, cần cài C# Dev Kitbản prerelease mới nhất của extension C# (2.79.8 trở lên)
  • Tạo tệp .cs rồi viết mã ngay
  • Chạy dotnet run hello.cs trong terminal
  • Khi cần, chuyển sang project bằng dotnet project convert hello.cs

Tìm hiểu thêm

Kế hoạch sắp tới

  • Dự kiến sẽ tiếp tục cải thiện hỗ trợ file-based app trong VS CodeIntelliSense cho directive, cùng với hiệu năng và khả năng debug
  • Các tính năng bổ sung như hỗ trợ nhiều tệp và cải thiện tốc độ thực thi cũng đang được phát triển
  • dotnet run app.cs giúp C# dễ tiếp cận hơn, đồng thời vẫn giữ nguyên sức mạnh của .NET
  • Tạo nền tảng để chuyển đổi nhanh hơn từ prototyping, giáo dục đến phát triển production

4 bình luận

 
rkttu 2025-08-18

Trải nghiệm DX cung cấp tính năng tự động hoàn thành dựa trên File-based App hiện đã có trong phiên bản mới nhất của tiện ích mở rộng C#, nhưng trước đây Microsoft không phát hành tiện ích này ở nơi nào khác ngoài VS Code Marketplace.

Để giải quyết sự bất tiện này, tôi đã tách riêng phần C# Extension của C# Dev Kit (phần theo giấy phép MIT), thiết lập autobuild/auto-publish để đăng lên OpenVSX, và chia sẻ một video demo đơn giản dựa trên Kiro.

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

 
ndrgrd 2025-05-31

Trước đây khi dùng tính năng C# Interactive thì không thể sử dụng các gói chưa được cài trên máy cục bộ, có vẻ giờ đã được cải thiện rồi.

 
GN⁺ 2025-05-30
Ý kiến trên Hacker News
  • Đây có vẻ là một tính năng có thể ảnh hưởng lớn đến năng suất của lập trình viên .NET, nên cũng thấy tiếc vì không hiểu sao đến tận bây giờ mới xuất hiện. Ngoài ra còn có một tính năng tôi thực sự muốn trong các dự án .NET: khả năng dễ dàng định nghĩa command tùy chỉnh theo từng project, kiểu như npm run <command> thì sẽ rất hay
  • Thật thú vị khi họ quảng bá mạnh việc dùng cái này cùng shebang, cách tiếp cận ấy khá hấp dẫn. Go trước khi có module cũng rất hợp cho kiểu dùng như script thế này, và tôi nhớ Ubuntu cũng từng dùng theo kiểu đó, nhưng các tác giả Go lại không mấy ủng hộ việc dùng Go như một ngôn ngữ scripting
    • Không hẳn là các tác giả Go phản đối, mà đúng hơn là họ khuyến khích dùng Go trước hết như một ngôn ngữ lập trình. Từ lâu đã có thể dùng Go như script khá dễ bằng các công cụ như gorun (https://github.com/erning/gorun), và gần đây còn hỗ trợ chạy một phát luôn bằng cách nhận tag trực tiếp như go run github.com/kardianos/json/cmd/jsondiff@v1.0.1, tính năng này khá hay
    • Tôi khá tò mò không biết từ “shebang” được dùng từ đâu và từ bao giờ. Khi học đại học cũng như ở khu vực miền Nam vào giai đoạn thập niên 90 đến đầu 2000, tôi thường thấy người ta gọi là hashbang. Tôi chỉ mới nghe từ shebang khi C# bắt đầu phổ biến, dù thực ra đây là thuật ngữ có từ trước rất lâu, chỉ là quanh tôi trước đó không ai dùng
    • Hồi còn làm ở một công ty .NET, có người đột nhiên lại viết script automation bằng bash. Trong đội không ai có chuyên môn để bảo trì lâu dài kiểu script đó, mà ngay từ đầu chất lượng cũng không tốt. Tôi đã không hiểu tại sao họ không đơn giản viết luôn công cụ đó bằng C#. Với tính năng này, C# có thể trở thành một lựa chọn thực tế hơn nhiều
    • Với cargo của Rust cũng làm được kiểu này, chỉ là chưa được hỗ trợ chính thức https://rust-lang.github.io/rfcs/3424-cargo-script.html
  • Bản thân tính năng thì rất tuyệt, nhưng ngay cả khi đã ở trạng thái compiled thì startup overhead vẫn khoảng 0.5 giây, nên với nhiều ứng dụng đây là một nhược điểm khiến nó không phù hợp. Dù vậy shell scripting phụ thuộc vào bash cũng có những giới hạn riêng, thời của perl thì đã qua rồi, còn Ruby vẫn là thứ tốt nhất cho mục đích này nên tôi vẫn dùng tiếp. Gần đây tôi chuyển vài script sang Swift; do về cơ bản nó theo kiểu interpreter nên nhanh hơn hẳn, còn executable đã biên dịch thì chạy gần như ngay lập tức nên rất ấn tượng. Tôi thậm chí còn tự làm một compiler có cache cho ứng dụng CLI Swift (https://github.com/jrz/tools). Nhân tiện, dotnet run vốn đã cache kết quả biên dịch nên không cần thêm lớp cache riêng nào nữa (muốn tắt thì dùng --no-build, muốn xem đường dẫn binary thì dùng --artifacts-path)
    • Tôi tò mò con số 0.5 giây đó lấy từ đâu, vì tôi thử với hello world thì ra 63ms. Xem benchmark thư viện CLI của neuecc (https://neuecc.medium.com/consoleappframework-v5-zero-overhead-native-aot-compatible-cli-framework-for-c-8f496df8d9d1) thì chẳng có cái nào chạm tới 0.5 giây cả. Nhân tiện bạn có nhắc Swift về cơ bản là interpreter, còn .NET JIT thì là tiered JIT nên không tạo code ngay một lần mà đi qua nhiều giai đoạn
    • Tôi nghe nói dotnet ở phiên bản 10 hoặc 11 sẽ có chế độ interpreted hoàn chỉnh, không biết chế độ đó có áp dụng được cho trường hợp dùng này không https://github.com/dotnet/runtime/issues/112748
    • Nếu chỉ cần có chút độ trễ lúc startup dù đã compile, thì tôi lại thắc mắc vì sao python có thể phổ biến đến thế trong mảng này
    • Tính năng này hiện vẫn mới ở giai đoạn preview ban đầu. Trong nhiều buổi công bố họ cũng đã nói là biết vấn đề tốc độ startup và đang cải thiện
    • Nếu muốn startup nhanh thì có thể dễ dàng chuyển sang native code theo hướng dẫn https://learn.microsoft.com/en-us/dotnet/core/deploying/
  • Hơi tiếc là gần như không nhắc đến các dự án CSX/VBX https://ttu.github.io/dotnet-script/, và cũng lạ khi có vẻ họ quyết định theo hướng không tương thích với cách xử lý dependency của F# script trong C# runtime https://learn.microsoft.com/en-us/dotnet/fsharp/tools/fsharp-interactive/
    • Về ý kiến cho rằng những nỗ lực như CSX/VBX chưa được phản ánh, thực tế là tài liệu chính thức vẫn có nhắc tới nhiều cách làm và công cụ khác nhau https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#existing-ways-to-run-c#-without-projects
    • Tôi muốn hỏi cụ thể ý “không tương thích với F#” là gì. Nếu là đang nói đến khác biệt cú pháp thì đó là chủ đích: họ không muốn tạo ra một dialect script riêng cho C#, nên cố tình chặn một số thứ như import file. Đây là do đặc tính của C#
  • Kotlin cũng có tính năng tương tự, https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md (ở đây file bắt buộc phải có đuôi *.main.kts mới chạy được). Cách này rất tốt cho các script nhỏ hoặc để làm prototype, và cũng thực dụng khi muốn tận dụng các tính năng của JVM. Dù vậy, với các script nhỏ thì Ruby vẫn là thứ tiện nhất, đặc biệt cú pháp backtick khi chạy chương trình ngoài thực sự rất tiện
  • Có thể dùng shebang để chạy script C# như script bash https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts
    • Tôi đã thử shebang trên image sdk .net10 preview 4 để chạy trực tiếp file nhưng ban đầu không được, còn dotnet run <file> thì vẫn chạy. Sau khi cập nhật thì hoạt động bình thường; nguyên nhân là file dùng xuống dòng CRLF thay vì LF
    • Tôi rất mừng vì giờ đã có thể viết script có type safety. Nhân tiện, trên macOS thì nên dùng #!/usr/local/share/dotnet/dotnet run hoặc #!/usr/bin/env -S dotnet run cho shebang
  • Trông có vẻ như một công cụ có thể thay thế PowerShell. PowerShell gần như đang có xu hướng trở thành một ngôn ngữ chỉ dành cho ChatGPT. Ở phần lớn công ty, các script viết bằng PowerShell giữ vai trò cốt lõi trong hạ tầng, nhưng thường rơi vào trạng thái gần như “chỉ đọc”
    • Không chỉ PowerShell mà có vẻ nó còn có tiềm năng thay thế phạm vi rộng hơn nhiều. Nếu là team .NET thì có lẽ chẳng cần đụng tới Python hay shell script nữa, chỉ cần thêm shebang ở đầu rồi dán đoạn C# vào là có thể xử lý gần như mọi loại script. Ngay cả dịch vụ test cũng không cần viết bằng express.js, làm nhanh bằng ASP.NET minimal API là xong
    • Có lẽ các quản trị viên hệ thống Windows là nhóm sử dụng script do ChatGPT viết với quy mô lớn nhất. Nếu tôi còn làm admin như trước, xét theo chất lượng tài liệu chính thức của MS, chắc chắn tôi cũng sẽ dùng
    • Cũng có thể gọi code C# từ PowerShell https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/add-type?view=powershell-7.5
    • Việc đưa toàn bộ hạ tầng vào script PowerShell thực tế không hề dễ, và làm vậy chỉ tạo ra đại hỗn loạn. Thật ra chỉ cần vượt quá vài hàm là C# đã hiệu quả hơn nhiều mà rào cản tiếp cận cũng gần như không có. PowerShell tối ưu cho các script ad-hoc quy mô nhỏ, và đang chiếm vị trí kiểu “ngôn ngữ script mặc định của Windows” giống VBScript ngày xưa
    • Vì PowerShell có thể chạy trực tiếp code .NET nên theo một nghĩa nào đó nó còn mở rộng thêm trải nghiệm PowerShell
  • Có cảm giác như nó gần như thay thế được chức năng của NetPad, và chỉ cần thêm debug nữa là LINQPad có thể bị đẩy ra khỏi tuyến đầu. Trước đây tôi cũng hưởng lợi rất nhiều từ LINQPad, nhưng trải nghiệm text editor của nó đến giờ vẫn quá bất tiện so với thời đại này; để viết hay chỉnh sửa code nghiêm túc thì còn nhiều giới hạn
    • Với tôi, mục đích cốt lõi của LINQPad là tương tác với DB hoặc khám phá giá trị bằng .dump(). dotnet run lần này có vẻ lại là công cụ bổ trợ cho đúng mảng đó. Trước đây tôi từng ở một nơi cực ghét PowerShell nên gần như xử lý toàn bộ scripting bằng LINQPad; trong bối cảnh như vậy thì nó khá hữu dụng
    • LINQPad là một sản phẩm độc đáo trong hệ .NET, nhưng riêng phần text editor thì nhiều lúc gần như thuộc hàng tệ nhất từng dùng. Sẽ tốt hơn nếu thay bằng editor như neovim hay monaco. Các thứ như hiển thị bảng hay độ snappy thì rất tốt, nhưng so với công nghệ “notebook” ngày nay như Jupyter Notebook thì phạm vi ứng dụng hẹp hơn nhiều. Có lẽ cũng vì đây là sản phẩm của một lập trình viên độc lập. Dù vậy, khi phải xử lý dữ liệu SQL hằng ngày trong công việc thì LINQPad vẫn là số một
    • Có lẽ LINQPad sẽ chưa bị thay thế ngay. Tôi nghĩ một nửa sức cạnh tranh của nó nằm ở UI. Tôi cũng tò mò trải nghiệm dùng dotnet run trong VSCode hay Visual Studio sẽ giống LINQPad đến mức nào. Điểm mạnh của LINQPad là khả năng trực quan hóa kết quả; nếu dotnet run chỉ in ra text hoặc cần quá nhiều plugin bổ sung thì nhu cầu với LINQPad vẫn sẽ còn. Nếu chỉ cần kiểm tra cú pháp các thứ thì dotnet run có thể là lựa chọn tốt hơn; bản thân tôi cũng thỉnh thoảng mang những cú pháp dễ nhầm vào LINQPad để thử
    • Nếu không làm đủ cả GUI lẫn các extension point thì khó mà thay thế LINQPad ngay được
  • Tôi thật sự rất mong chờ tính năng này. Có vẻ nó có thể thay thế một phần các script PowerShell tôi đang dùng trong pipeline CI/CD. Tôi thích cả PowerShell lẫn Bash, nhưng rõ ràng có những việc mà suy nghĩ bằng ngôn ngữ cú pháp họ C sẽ hiệu quả hơn nhiều trong đầu, và tính năng này có thể lấp đúng khoảng trống đó
  • Trong đề xuất gốc (https://github.com/dotnet/sdk/blob/main/documentation/general/dotnet-run-file.md) còn có thêm khá nhiều thông tin, đặc biệt là về xử lý nhiều file và các chi tiết triển khai như project file ngầm định
 
rkttu 2025-05-30

Mình cũng đã làm 2 ví dụ thực tế liên quan đến tính năng này nên chia sẻ ở phần trả lời. Đây là mã mẫu cho ứng dụng GUI trên Windows và macOS sử dụng máy chủ MCP và Avalonia. 😊

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