Skip to content

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 .cs duy nhất, chạy bằng dotnet run hello.cs, hợp để thử nhanh một ý tưởng. Cách thứ hai là một project tạo bằng dotnet 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ục bin và obj sinh 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ằng dotnet run và giải thích được từng file trong project.
  • Đọc hiểu file .csproj mặc định: TargetFramework, ImplicitUsings, Nullable.
  • Phân biệt dotnet build và dotnet run, biết bin và obj chứa gì và vì sao không đưa chúng lên Git.
  • Dùng Console.WriteLine, Console.ReadLine và 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:

csharp
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:

bash
dotnet run hello.cs -- DSC
text
Xin 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àm Main ẩn.
  • args là mảng chuỗi chứa tham số dòng lệnh. Mọi thứ đứng sau -- được chuyển vào args, nên ở đây args[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 ​

bash
dotnet new console -n HelloCrm
cd HelloCrm
dotnet run
text
Hello, World!

dotnet new console tạo một thư mục HelloCrm có đúng hai file:

text
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 ​

xml
<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 = ExeBuild ra chương trình chạy được. Thư viện (bài 0.4) không có dòng này.
TargetFramework = net10.0Project chạy trên .NET 10.
ImplicitUsings = enableTự 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 = enableTrì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:

text
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 build
  • dotnet build chỉ 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 run gồ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:

bash
dotnet new gitignore

Lệ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:

csharp
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.Write in 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ằng int.Parse.
  • Dấu ! sau ReadLine() 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ẻ:

text
+------------------------------+
| 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.

csharp
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ục bin, obj trong 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 .cs thì 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 ​

  1. Một file để thử nhanh, project cho mọi thứ còn lại.
  2. .csproj là bản mô tả project. Đọc được 5 dòng của nó là đủ cho cả giai đoạn 1.
  3. Đọ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 ​

Điều hướng ​

Phát hành theo giấy phép MIT.