Thời lượng: 120′ | Cấu trúc: 50% Lý thuyết – 50% Thực hành | Công cụ: VS Code + .NET SDK
I) Mục tiêu học tập
- Hiểu rõ value type vs reference type, vùng nhớ stack/heap, truyền tham trị/tham chiếu.
- Nắm vững ép kiểu (ngầm định/tường minh),
Convert
,Parse
/TryParse
và các lỗi phổ biến. - Viết được I/O console:
Write/WriteLine
,ReadLine
, kiểm tra và làm sạch dữ liệu đầu vào. - Áp dụng thành thạo if/else, switch (kể cả pattern matching cơ bản).
- Thực hành for, while, do..while, foreach trong các bài toán thực tế.
- Làm việc với mảng 1 chiều: tạo, duyệt, tìm kiếm, tổng hợp (sum/min/max/avg).
II) Lý thuyết chi tiết (≈60′)
1) Value type vs Reference type, Stack vs Heap
Value types (lưu giá trị trực tiếp, thường trên stack): bool, byte, short, int, long, float, double, decimal, char, struct, enum
.
Reference types (lưu tham chiếu tới đối tượng trên heap): string, object, array, class, interface, delegate
.
// Value: sao chép giá trị
int a = 10;
int b = a; // b = 10, thay đổi b không ảnh hưởng a
b++;
Console.WriteLine($"{a}, {b}"); // 10, 11
// Reference: sao chép tham chiếu (cùng trỏ 1 vùng heap)
int[] x = {1,2,3};
int[] y = x;
y[0] = 100;
Console.WriteLine(x[0]); // 100
- Stack: cấp phát/thu hồi nhanh, dùng cho biến cục bộ/value type.
- Heap: lưu đối tượng reference; GC (garbage collector) thu hồi khi không còn tham chiếu.
2) Ép kiểu & chuyển đổi
- Widening (ngầm định): an toàn (ví dụ
int → long → double
). - Narrowing (tường minh): có thể mất dữ liệu (cần cast).
// Widening
int iv = 100;
double dv = iv; // OK
// Narrowing
double d = 9.8;
int i = (int)d; // 9 (mất phần thập phân)
// Convert/Parse/TryParse
string s = "123";
int n1 = Convert.ToInt32(s); // an toàn hơn cast từ string
int n2 = int.Parse(s); // ném lỗi nếu s không hợp lệ
if (int.TryParse("abc", out int n3))
Console.WriteLine(n3);
else
Console.WriteLine("Không phải số hợp lệ");
Khuyến nghị: dùng TryParse
cho input người dùng; dùng decimal
cho tiền tệ để giảm sai số nhị phân.
3) Thứ tự ưu tiên toán tử (Operator precedence)
- Nhân/Chia/Phần trăm (×, ÷, %) ưu tiên hơn Cộng/Trừ.
- Dùng ngoặc
()
để rõ ràng và đúng ý đồ.
int r1 = 2 + 3 * 4; // 14
int r2 = (2 + 3) * 4; // 20
bool b = 3 + 2 * 2 == 7; // true
4) I/O console & làm sạch dữ liệu
Console.Write("Nhập tuổi: ");
string raw = Console.ReadLine()?.Trim();
if (!int.TryParse(raw, out int age) || age < 0) {
Console.WriteLine("Tuổi không hợp lệ!");
} else {
Console.WriteLine($"Bạn {age} tuổi.");
}
5) Cấu trúc điều khiển: if/else, switch
double avg = 8.2;
if (avg >= 8.5) Console.WriteLine("Giỏi");
else if (avg >= 7.0) Console.WriteLine("Khá");
else Console.WriteLine("Trung bình");
// switch truyền thống
int day = 5;
switch (day) {
case 1: Console.WriteLine("Mon"); break;
case 2: Console.WriteLine("Tue"); break;
default: Console.WriteLine("Other"); break;
}
// switch expression (pattern matching cơ bản)
string level = avg switch {
>= 8.5 => "A",
>= 7.0 => "B",
_ => "C"
};
Console.WriteLine(level);
Ghi nhớ: C# không “fall‑through” như C/C++; mỗi case
nên kết thúc bằng break
(hoặc trả về/throw).
6) Vòng lặp: for, while, do..while, foreach
for (int k = 1; k <= 5; k++) Console.Write($"{k} "); // 1 2 3 4 5
int w = 3;
while (w > 0) { Console.Write(w--); } // 321
int d2 = 0;
do { Console.Write(d2++); } while (d2 < 3); // 012
int[] arr = {1, 2, 3};
foreach (var v in arr) Console.WriteLine(v); // 1 2 3
7) Mảng 1 chiều
int[] a = {3, 7, 2, 9, 5};
Console.WriteLine(a.Length);
int sum = 0, min = a[0], max = a[0];
foreach (var v in a) {
sum += v;
if (v < min) min = v;
if (v > max) max = v;
}
double avg2 = (double)sum / a.Length;
Console.WriteLine($"Sum={sum}, Min={min}, Max={max}, Avg={avg2:F2}");
III) Thực hành theo bước (≈60′)
Lab 1 – I/O & ép kiểu an toàn (10′)
Nhập subtotal (decimal), tính VAT 8% và tổng tiền (định dạng 2 chữ số thập phân).
Console.Write("Subtotal: ");
if (!decimal.TryParse(Console.ReadLine(), out decimal sub) || sub < 0) {
Console.WriteLine("Giá trị không hợp lệ!"); return;
}
const decimal VAT = 0.08m;
decimal total = sub + sub * VAT;
Console.WriteLine($"Total = {total:F2}");
Lab 2 – Điều kiện & kiểm tra biên (15′)
Nhập điểm (0..100) → in xếp loại: A (≥85), B (70..84), C (<70). Nếu ngoài khoảng, báo lỗi.Gợi ý/Đáp án
Console.Write("Điểm: ");
if (!int.TryParse(Console.ReadLine(), out int s) || s < 0 || s > 100) {
Console.WriteLine("Điểm không hợp lệ"); return;
}
string grade = s switch { >= 85 => "A", >= 70 => "B", _ => "C" };
Console.WriteLine($"Xếp loại: {grade}");
Lab 3 – Vòng lặp ứng dụng (20′)
- Bảng cửu chương: nhập
n
(2..9) → inn x 1..10
. - Tổng 1..N: tính bằng
for
và bằng công thứcN*(N+1)/2
. - Đếm chữ số: nhập số nguyên dương, đếm số chữ số bằng
while
.
Gợi ý nhanh
// 1) Cửu chương
Console.Write("n = ");
int n = int.Parse(Console.ReadLine());
for (int i = 1; i <= 10; i++) Console.WriteLine($"{n} x {i} = {n*i}");
// 2) Tổng 1..N
Console.Write("N = ");
int N = int.Parse(Console.ReadLine());
long sumLoop = 0; for (int i = 1; i <= N; i++) sumLoop += i;
long sumFormula = (long)N*(N+1)/2;
Console.WriteLine($"Loop={sumLoop}, Formula={sumFormula}");
// 3) Đếm chữ số
Console.Write("Số dương: ");
int x = int.Parse(Console.ReadLine());
int cnt = 0; do { cnt++; x/=10; } while (x > 0);
Console.WriteLine($"Số chữ số: {cnt}");
Lab 4 – Mảng 1 chiều & truy vấn dữ liệu (15′)
Nhập mảng số nguyên (cách nhau bởi dấu cách), in sum/min/max/avg; kiểm tra một giá trị có thuộc mảng không.Đáp án mẫu
using System;
using System.Linq;
Console.Write("Nhập mảng: ");
int[] arr = Console.ReadLine()
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse)
.ToArray();
int sum = 0, min = arr[0], max = arr[0];
foreach (var v in arr) { sum += v; if (v < min) min = v; if (v > max) max = v; }
double avg = (double)sum / arr.Length;
Console.WriteLine($"Sum={sum}, Min={min}, Max={max}, Avg={avg:F2}");
Console.Write("Tìm giá trị: ");
int target = int.Parse(Console.ReadLine());
bool found = arr.Contains(target);
Console.WriteLine(found ? "Có trong mảng" : "Không có");
IV) Quiz nhanh (5 câu)
- Khác nhau giữa
int.Parse
vàint.TryParse
? Khi nào dùng cái nào? - Cho ví dụ cần narrowing cast và rủi ro kèm theo.
- Điểm khác giữa
while
vàdo..while
(thứ tự kiểm tra điều kiện)? - Vì sao nên dùng
decimal
cho tiền tệ thay vìdouble
? foreach
tiện ở chỗ nào so vớifor
khi duyệt mảng?
V) Bài tập về nhà
- Số nguyên tố: viết hàm kiểm tra và in tất cả số nguyên tố ≤ N (dùng vòng lặp).
- Thống kê mảng số thực: nhập mảng, in median và standard deviation (gợi ý: sắp xếp + công thức σ).
- Tối ưu vòng lặp: benchmark tổng 1..N bằng vòng lặp vs công thức (thử N lớn), so sánh thời gian.
VI) Ghi chú cho giảng viên
- Nhấn mạnh lỗi phổ biến: Parse fail,
IndexOutOfRangeException
, nhầmfloat/double/decimal
, quênTrim()
input. - Lớp nhanh: thêm mảng 2D (bảng điểm) và switch nâng cao (pattern matching với
when
). - Khuyến khích sinh viên dùng
TryParse
cho mọi input từ người dùng.