0.2 - Chương trình C# đầu tiên
Tóm tắt: Có hai cách chạy C# trên .NET 10. Cách thứ nhất là một file
.csduy nhất, chạy bằngdotnet run hello.cs, hợp để thử nhanh một ý tưởng. Cách thứ hai là một project tạo bằngdotnet new console, đây là thứ bạn sẽ dùng cho mọi bài từ Module 1 trở đi. Bài này đi qua cả hai, giải thích từng dòng của file.csproj, các thư mụcbinvàobjsinh ra khi build, và cách đọc dữ liệu người dùng nhập từ bàn phím.
Mục tiêu bài học
Sau bài này bạn có thể:
- Viết và chạy một chương trình C# một file bằng
dotnet run. - Tạo project console bằng
dotnet new console, chạy bằngdotnet runvà giải thích được từng file trong project. - Đọc hiểu file
.csprojmặc định:TargetFramework,ImplicitUsings,Nullable. - Phân biệt
dotnet buildvàdotnet run, biếtbinvàobjchứa gì và vì sao không đưa chúng lên Git. - Dùng
Console.WriteLine,Console.ReadLinevà chuỗi nội suy$"...".
Cần biết trước: đã làm xong 0.1 — Cài .NET SDK và trình soạn code.
Nội dung bài học
0.2.1 — Một file, một lệnh
Tạo một thư mục trống, mở nó bằng VS Code (File > Open Folder). Tạo file hello.cs với nội dung:
var name = args.Length > 0 ? args[0] : "CLB";
Console.WriteLine($"Xin chào {name}! Hôm nay là {DateTime.Now:dd/MM/yyyy}.");Mở terminal trong VS Code (Ctrl+`) và chạy:
dotnet run hello.cs -- DSCXin chào DSC! Hôm nay là 17/10/2026.Lần chạy đầu mất vài giây vì dotnet phải biên dịch. Các lần sau nhanh hơn nhiều.
Có ba điều đáng để ý trong hai dòng code này:
- Không có
class, không cóMain. C# cho phép viết lệnh ngay ở đầu file, gọi là top-level statements. Trình biên dịch tự bọc chúng vào một hàmMainẩn. argslà mảng chuỗi chứa tham số dòng lệnh. Mọi thứ đứng sau--được chuyển vàoargs, nên ở đâyargs[0]là"DSC".$"..."là chuỗi nội suy. Biểu thức trong{}được tính rồi chèn vào chuỗi.{DateTime.Now:dd/MM/yyyy}còn kèm định dạng ngày kiểu Việt Nam.
Cách chạy một file hợp để thử nhanh. Nhưng một ứng dụng thật có nhiều file, cần thư viện ngoài và cần test, nên từ đây trở đi ta dùng project.
0.2.2 — Tạo project console
dotnet new console -n HelloCrm
cd HelloCrm
dotnet runHello, World!dotnet new console tạo một thư mục HelloCrm có đúng hai file:
HelloCrm/
├── HelloCrm.csproj ← mô tả project: dùng .NET nào, cần thư viện gì
└── Program.cs ← code, hiện chỉ có Console.WriteLine("Hello, World!");Trong VS Code, bạn cũng có thể tạo project bằng Command Palette (Ctrl+Shift+P), chọn .NET: New Project..., rồi chọn Console App. Kết quả giống hệt lệnh trên.
0.2.3 — Đọc file .csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>| Dòng | Ý nghĩa |
|---|---|
Sdk="Microsoft.NET.Sdk" | Loại project. Web API ở bài 0.5 sẽ dùng Microsoft.NET.Sdk.Web. |
OutputType = Exe | Build ra chương trình chạy được. Thư viện (bài 0.4) không có dòng này. |
TargetFramework = net10.0 | Project chạy trên .NET 10. |
ImplicitUsings = enable | Tự thêm các using hay dùng như System, System.Linq, System.Collections.Generic. Nhờ vậy bạn viết được Console.WriteLine mà không cần using System;. |
Nullable = enable | Trình biên dịch cảnh báo khi bạn có thể dùng nhầm giá trị null. Bạn sẽ học kỹ ở bài 1.1. |
Thư viện ngoài (gói NuGet) cũng được khai báo trong file này. Bạn không cần sửa tay: lệnh dotnet package add <tên gói> tự thêm dòng <PackageReference>.
0.2.4 — build, run và hai thư mục lạ
Sau lần dotnet run đầu tiên, project có thêm hai thư mục:
HelloCrm/
├── bin/Debug/net10.0/
│ ├── HelloCrm.dll ← code đã biên dịch
│ ├── HelloCrm.exe ← file chạy (trên Windows)
│ ├── HelloCrm.pdb ← thông tin để debug (bài 0.3)
│ └── ...
└── obj/ ← file tạm của quá trình builddotnet buildchỉ biên dịch và báo lỗi nếu có. Dùng nó để kiểm tra code có hợp lệ không.dotnet rungồm build (nếu code đã đổi) rồi chạy luôn.
bin và obj sinh lại được bất cứ lúc nào, nên không bao giờ đưa chúng lên Git. Tạo sẵn file .gitignore chuẩn cho .NET bằng một lệnh:
dotnet new gitignoreLệnh này đặc biệt hữu ích khi bạn tới Module 3 (Git).
0.2.5 — Nhập dữ liệu từ bàn phím
Thay nội dung Program.cs bằng:
Console.Write("Bạn tên gì? ");
var name = Console.ReadLine();
Console.Write("Bạn sinh năm nào? ");
var birthYear = int.Parse(Console.ReadLine()!);
var age = DateTime.Today.Year - birthYear;
Console.WriteLine($"Chào {name}, năm nay bạn {age} tuổi.");Chạy bằng dotnet run, gõ tên, Enter, gõ năm sinh, Enter.
Console.Writein ra mà không xuống dòng, nên con trỏ đứng ngay sau câu hỏi.Console.ReadLine()luôn trả về chuỗi. Muốn tính toán thì phải chuyển sang số bằngint.Parse.- Dấu
!sauReadLine()nói với trình biên dịch "tôi chắc chắn giá trị này không null", để tắt cảnh báo nullable. Bài 1.1 sẽ giải thích vì sao có cảnh báo này.
Thử gõ chữ thay vì số ở câu hỏi năm sinh: chương trình sẽ sập. Bài 0.3 sẽ dùng chính lỗi đó để tập đọc thông báo lỗi và sửa bằng int.TryParse.
0.2.6 — Lỗi thường gặp
Couldn't find a project to run. Bạn đang đứng sai thư mục. dotnet run (không kèm tên file) tìm file .csproj ở thư mục hiện tại. Chạy cd HelloCrm trước.
Sửa code nhưng kết quả không đổi. Có thể file chưa được lưu. Bật File > Auto Save trong VS Code để khỏi quên.
Lỗi CS1002: ; expected. Thiếu dấu chấm phẩy cuối câu lệnh. Thông báo lỗi có dạng Program.cs(3,30), nghĩa là dòng 3, cột 30. Luôn đọc vị trí này trước khi đoán.
Lỗi CS0103: The name 'xyz' does not exist in the current context. Gõ sai tên biến, hoặc sai chữ hoa chữ thường. C# phân biệt name và Name.
Bài tập áp dụng
Bài 1 — Thẻ thành viên CLB
Viết chương trình hỏi họ tên, năm sinh và ban (Tech, Media, Event), rồi in ra một tấm thẻ:
+------------------------------+
| THẺ THÀNH VIÊN CLB |
| Họ tên: Nguyen Van An |
| Tuổi: 21 |
| Ban: Tech |
+------------------------------+Tiêu chí hoàn thành: cạnh phải của khung thẳng hàng với mọi độ dài tên dưới 20 ký tự.
Gợi ý và lời giải — Bài 1
Gợi ý. Trong chuỗi nội suy, {value,-28} căn trái giá trị trong một ô rộng 28 ký tự, phần thiếu được bù bằng dấu cách.
Lời giải.
Console.Write("Họ tên: ");
var fullName = Console.ReadLine();
Console.Write("Năm sinh: ");
var birthYear = int.Parse(Console.ReadLine()!);
Console.Write("Ban (Tech, Media, Event): ");
var team = Console.ReadLine();
var age = DateTime.Today.Year - birthYear;
Console.WriteLine();
Console.WriteLine("+------------------------------+");
Console.WriteLine($"| {"THẺ THÀNH VIÊN CLB",-28} |");
Console.WriteLine($"| {"Họ tên: " + fullName,-28} |");
Console.WriteLine($"| {"Tuổi: " + age,-28} |");
Console.WriteLine($"| {"Ban: " + team,-28} |");
Console.WriteLine("+------------------------------+");Vì sao không tự đếm dấu cách? Vì độ dài tên thay đổi theo người nhập. Để định dạng làm việc đó thì code ngắn hơn và không sai khi tên dài, ngắn khác nhau.
Tên dài hơn 28 ký tự sẽ làm lệch khung. Thử tự xử lý trường hợp này bằng cách cắt chuỗi: fullName[..20] lấy 20 ký tự đầu, nhưng sẽ báo lỗi nếu tên ngắn hơn 20 ký tự, nên phải kiểm tra fullName.Length trước.
Bài 2 — Một file hay một project?
Chạy lời giải Bài 1 theo cả hai cách: lưu thành the.cs rồi dotnet run the.cs, và tạo project TheThanhVien rồi dán code vào Program.cs.
Tiêu chí hoàn thành: bạn kể ra được ít nhất hai khác biệt giữa hai cách, dựa trên những gì quan sát được.
Gợi ý và lời giải — Bài 2
Những khác biệt bạn có thể thấy:
- Cách một file không có
.csproj, không có thư mụcbin,objtrong thư mục của bạn. .NET vẫn build, nhưng để kết quả ở thư mục tạm của hệ thống. - Project tách được code ra nhiều file, thêm thư viện bằng
dotnet package add, và có test đi kèm (bài 0.4). - Project mở được trong Visual Studio, Rider với đầy đủ tính năng. Một file
.csthì các công cụ này hỗ trợ hạn chế hơn.
Kết luận thực tế: một file để thử nhanh, để viết script nhỏ. Mọi thứ còn lại dùng project.
Tự kiểm tra
dotnet build và dotnet run khác nhau thế nào?
dotnet build chỉ biên dịch và báo lỗi nếu có. dotnet run build (nếu cần) rồi chạy chương trình luôn.
Vì sao viết được Console.WriteLine mà không cần using System?
Vì .csproj bật ImplicitUsings. Trình biên dịch tự thêm một nhóm using hay dùng, trong đó có System.
Có nên đưa thư mục bin và obj lên Git không?
Không. Hai thư mục này chứa kết quả build và file tạm, sinh lại được bằng dotnet build. Dùng dotnet new gitignore để Git tự bỏ qua chúng.
Console.ReadLine trả về kiểu gì?
Luôn trả về chuỗi (string?, có thể null khi không còn dữ liệu vào). Muốn dùng như số thì phải chuyển kiểu, ví dụ int.Parse hoặc an toàn hơn là int.TryParse.
Kết luận
- Một file để thử nhanh, project cho mọi thứ còn lại.
.csprojlà bản mô tả project. Đọc được 5 dòng của nó là đủ cho cả giai đoạn 1.- Đọc vị trí lỗi
(dòng,cột)trước khi đoán. Thói quen này tiết kiệm nhiều thời gian nhất cho người mới.
Tham khảo
- Tutorial: Create a .NET console application using Visual Studio Code
- File-based apps
- Top-level statements
- String interpolation
- dotnet new
Điều hướng
- Bài trước: 0.1 — Cài .NET SDK và trình soạn code
- Bài tiếp theo: 0.3 — Debug và đọc thông báo lỗi
- Về module: Trang mục lục