Bài tập 1(B9_C#): Xây dựng lớp SinhVien đầy đủ

BƯỚC 1: TẠO PROJECT MỚI TRONG VISUAL STUDIO

1.1. Mở Visual Studio

  • Nhấp đúp vào icon Visual Studio trên Desktop

  • Hoặc vào Start Menu → Visual Studio 2022

1.2. Tạo Project mới

File → New → Project

Hoặc nhấn tổ hợp phím: Ctrl + Shift + N

1.3. Chọn loại Project

  • Gõ “Console” vào ô tìm kiếm

  • Chọn Console App (.NET Core) hoặc Console App (.NET Framework)

  • Nhấn Next

1.4. Đặt tên và cấu hình

Project name: QuanLySinhVien
Location: Chọn thư mục lưu trữ (ví dụ: D:\CSharp\)
Solution name: QuanLySinhVien (giữ nguyên)
  • Nhấn Next

1.5. Chọn Framework

  • Chọn .NET 6.0 hoặc .NET 8.0 (LTS – Long Term Support)

  • Nhấn Create

BƯỚC 2: TẠO CẤU TRÚC THƯ MỤC

2.1. Tạo thư mục Models

  1. Trong Solution Explorer, click chuột phải vào tên Project

  2. Chọn Add → New Folder

  3. Đặt tên: Models

  4. Nhấn Enter

2.2. Tạo file SinhVien.cs

  1. Click chuột phải vào thư mục Models

  2. Chọn Add → Class

  3. Đặt tên: SinhVien.cs

  4. Nhấn Add

BƯỚC 3: VIẾT MÃ CHO LỚP SINHVIEN

3.1. Khai báo Namespace và Class

Mở file SinhVien.cs và viết:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuanLySinhVien.Models
{
    public class SinhVien
    {
        // Code sẽ viết ở đây
    }
}

Giải thích:

  • using các namespace cần thiết

  • namespace QuanLySinhVien.Models – nhóm các lớp liên quan

  • public class SinhVien – khai báo lớp công khai

3.2. Khai báo Fields (Trường dữ liệu)

Thêm vào bên trong class:

public class SinhVien
{
    // === FIELDS (Trường dữ liệu) ===
    private string _maSV;         // Mã sinh viên
    private string _hoTen;        // Họ tên
    private DateTime _ngaySinh;   // Ngày sinh
    private double _diemToan;     // Điểm Toán
    private double _diemLy;       // Điểm Lý
    private double _diemHoa;      // Điểm Hóa
    private static int _tongSo = 0; // Tổng số sinh viên (dùng chung)
}

Giải thích:

  • private – chỉ truy cập được bên trong lớp (tính đóng gói)

  • _ (underscore) – quy ước đặt tên cho field

  • static – biến dùng chung cho tất cả đối tượng

  • DateTime – kiểu dữ liệu ngày tháng

3.3. Khai báo Properties (Thuộc tính)

Thêm vào sau phần Fields:

public class SinhVien
{
    // === FIELDS ===
    private string _maSV;
    private string _hoTen;
    private DateTime _ngaySinh;
    private double _diemToan;
    private double _diemLy;
    private double _diemHoa;
    private static int _tongSo = 0;

    // === PROPERTIES (Thuộc tính) ===
    
    // 1. Mã sinh viên - Có kiểm tra rỗng
    public string MaSV
    {
        get { return _maSV; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Mã sinh viên không được để trống!");
            _maSV = value.Trim();
        }
    }

    // 2. Họ tên - Có kiểm tra rỗng và tự động chuẩn hóa
    public string HoTen
    {
        get { return _hoTen; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Họ tên không được để trống!");
            
            // Chuẩn hóa: Xóa khoảng trắng thừa
            string ten = value.Trim();
            while (ten.Contains("  "))
                ten = ten.Replace("  ", " ");
            
            // Viết hoa chữ cái đầu mỗi từ
            string[] words = ten.Split(' ');
            for (int i = 0; i < words.Length; i++)
            {
                if (!string.IsNullOrEmpty(words[i]))
                {
                    words[i] = char.ToUpper(words[i][0]) + words[i].Substring(1).ToLower();
                }
            }
            _hoTen = string.Join(" ", words);
        }
    }

    // 3. Ngày sinh - Có kiểm tra hợp lệ
    public DateTime NgaySinh
    {
        get { return _ngaySinh; }
        set
        {
            // Kiểm tra ngày sinh không được trong tương lai
            if (value > DateTime.Now)
                throw new ArgumentException("Ngày sinh không được trong tương lai!");
            
            // Kiểm tra ngày sinh quá xa (trước năm 1900)
            if (value < new DateTime(1900, 1, 1))
                throw new ArgumentException("Ngày sinh không hợp lệ!");
            
            _ngaySinh = value;
        }
    }

    // 4. Điểm Toán - Có kiểm tra trong khoảng 0-10
    public double DiemToan
    {
        get { return _diemToan; }
        set
        {
            if (value < 0 || value > 10)
                throw new ArgumentException("Điểm Toán phải từ 0 đến 10!");
            _diemToan = Math.Round(value, 1); // Làm tròn 1 chữ số thập phân
        }
    }

    // 5. Điểm Lý - Có kiểm tra trong khoảng 0-10
    public double DiemLy
    {
        get { return _diemLy; }
        set
        {
            if (value < 0 || value > 10)
                throw new ArgumentException("Điểm Lý phải từ 0 đến 10!");
            _diemLy = Math.Round(value, 1);
        }
    }

    // 6. Điểm Hóa - Có kiểm tra trong khoảng 0-10
    public double DiemHoa
    {
        get { return _diemHoa; }
        set
        {
            if (value < 0 || value > 10)
                throw new ArgumentException("Điểm Hóa phải từ 0 đến 10!");
            _diemHoa = Math.Round(value, 1);
        }
    }

    // 7. Tổng số sinh viên (Read-only - chỉ đọc)
    public static int TongSo
    {
        get { return _tongSo; }
    }
}

Giải thích từng Property:

 
PropertyÝ nghĩaLogic kiểm tra
MaSVMã sinh viênKhông được rỗng, tự động Trim
HoTenHọ tênKhông rỗng + Chuẩn hóa (xóa khoảng trắng, viết hoa đầu từ)
NgaySinhNgày sinhKhông trong tương lai, không trước 1900
DiemToanĐiểm ToánTừ 0 đến 10, làm tròn 1 số
DiemLyĐiểm LýTừ 0 đến 10, làm tròn 1 số
DiemHoaĐiểm HóaTừ 0 đến 10, làm tròn 1 số
TongSoTổng số SVChỉ đọc (Read-only), dùng chung (static)

3.4. Khai báo Computed Properties (Thuộc tính tính toán)

public class SinhVien
{
    // ... Fields và Properties ở trên ...

    // === COMPUTED PROPERTIES (Thuộc tính tính toán) ===
    
    // Điểm trung bình - Tự động tính từ 3 môn
    public double DiemTrungBinh
    {
        get
        {
            return Math.Round((_diemToan + _diemLy + _diemHoa) / 3.0, 2);
        }
    }

    // Xếp loại - Tự động phân loại dựa trên điểm TB
    public string XepLoai
    {
        get
        {
            double dtb = DiemTrungBinh;
            if (dtb >= 9.0) return "Xuất sắc";
            if (dtb >= 8.0) return "Giỏi";
            if (dtb >= 6.5) return "Khá";
            if (dtb >= 5.0) return "Trung bình";
            return "Yếu";
        }
    }

    // Tuổi - Tự động tính từ ngày sinh
    public int Tuoi
    {
        get
        {
            DateTime today = DateTime.Today;
            int age = today.Year - _ngaySinh.Year;
            
            // Nếu chưa qua sinh nhật trong năm nay thì trừ 1
            if (_ngaySinh.Date > today.AddYears(-age))
                age--;
            
            return age;
        }
    }
}

Giải thích:

  • Computed Property – Thuộc tính không lưu trữ, tự tính toán khi đọc

  • DiemTrungBinh – Tính trung bình 3 môn

  • XepLoai – Phân loại theo thang điểm

  • Tuoi – Tính từ ngày sinh


3.5. Khai báo Constructors (Phương thức khởi tạo)

public class SinhVien
{
    // ... Fields, Properties, Computed Properties ở trên ...

    // === CONSTRUCTORS (Phương thức khởi tạo) ===
    
    // Constructor 1: Không tham số (Default)
    public SinhVien()
    {
        _maSV = "SV" + (_tongSo + 1).ToString("D3");
        _hoTen = "Chưa xác định";
        _ngaySinh = new DateTime(2000, 1, 1);
        _diemToan = 0;
        _diemLy = 0;
        _diemHoa = 0;
        _tongSo++; // Tăng số lượng sinh viên
    }

    // Constructor 2: Có tham số đầy đủ
    public SinhVien(string maSV, string hoTen, DateTime ngaySinh, 
                    double diemToan, double diemLy, double diemHoa)
    {
        // Sử dụng Properties để có kiểm tra logic
        MaSV = maSV;
        HoTen = hoTen;
        NgaySinh = ngaySinh;
        DiemToan = diemToan;
        DiemLy = diemLy;
        DiemHoa = diemHoa;
        _tongSo++; // Tăng số lượng sinh viên
    }

    // Constructor 3: Chỉ có mã và tên (các giá trị khác mặc định)
    public SinhVien(string maSV, string hoTen) : this()
    {
        MaSV = maSV;
        HoTen = hoTen;
        // Ngày sinh, điểm giữ nguyên mặc định
    }
}

Giải thích:

ConstructorCách dùngMục đích
SinhVien()new SinhVien()Tạo SV mới với giá trị mặc định
SinhVien(ma, ten, ngay, toan, ly, hoa)new SinhVien("SV001", "A", ...)Tạo SV với đầy đủ thông tin
SinhVien(ma, ten)new SinhVien("SV001", "A")Tạo SV với mã và tên, các giá trị khác mặc định

3.6. Khai báo Methods (Phương thức)

public class SinhVien
{
    // ... Fields, Properties, Constructors ở trên ...

    // === METHODS (Phương thức) ===
    
    // 1. Hiển thị thông tin sinh viên
    public void HienThi()
    {
        Console.WriteLine("═══════════════════════════════════════");
        Console.WriteLine($"  Mã sinh viên  : {_maSV}");
        Console.WriteLine($"  Họ và tên     : {_hoTen}");
        Console.WriteLine($"  Ngày sinh     : {_ngaySinh:dd/MM/yyyy}");
        Console.WriteLine($"  Tuổi          : {Tuoi}");
        Console.WriteLine($"  Điểm Toán     : {_diemToan:F1}");
        Console.WriteLine($"  Điểm Lý       : {_diemLy:F1}");
        Console.WriteLine($"  Điểm Hóa      : {_diemHoa:F1}");
        Console.WriteLine($"  Điểm TB       : {DiemTrungBinh:F2}");
        Console.WriteLine($"  Xếp loại      : {XepLoai}");
        Console.WriteLine("═══════════════════════════════════════");
    }

    // 2. Hiển thị tóm tắt (1 dòng)
    public string HienThiTomTat()
    {
        return $"{_maSV,-10} | {_hoTen,-25} | {DiemTrungBinh,6:F2} | {XepLoai,-12}";
    }

    // 3. Kiểm tra sinh viên có đạt hay không
    public bool IsDat()
    {
        return DiemTrungBinh >= 5.0 && _diemToan >= 4.0 && _diemLy >= 4.0 && _diemHoa >= 4.0;
    }

    // 4. Kiểm tra sinh viên có giỏi hay không
    public bool IsGioi()
    {
        return DiemTrungBinh >= 8.0;
    }

    // 5. So sánh điểm TB với sinh viên khác
    public int SoSanhDiem(SinhVien other)
    {
        if (other == null) return 1;
        return this.DiemTrungBinh.CompareTo(other.DiemTrungBinh);
    }

    // 6. Override ToString() - hiển thị thông tin dạng chuỗi
    public override string ToString()
    {
        return $"{_maSV} - {_hoTen} - {DiemTrungBinh:F2} - {XepLoai}";
    }
}

Giải thích các phương thức:

MethodÝ nghĩa
HienThi()In đầy đủ thông tin ra màn hình
HienThiTomTat()Trả về 1 dòng tóm tắt
IsDat()Kiểm tra đạt hay không (TB>=5, không môn nào dưới 4)
IsGioi()Kiểm tra giỏi (TB>=8)
SoSanhDiem()So sánh điểm với SV khác
ToString()Ghi đè để hiển thị dạng chuỗi

BƯỚC 4: VIẾT CHƯƠNG TRÌNH KIỂM TRA (TEST)

Mở file Program.cs và viết code test:

using System;
using System.Collections.Generic;
using QuanLySinhVien.Models;

namespace QuanLySinhVien
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            Console.WriteLine("===== KIỂM TRA LỚP SINH VIÊN =====\n");

            // 1. Tạo đối tượng bằng Constructor mặc định
            Console.WriteLine("1. Constructor mặc định:");
            SinhVien sv1 = new SinhVien();
            sv1.HienThi();
            
            // 2. Tạo đối tượng bằng Constructor có tham số
            Console.WriteLine("\n2. Constructor có tham số:");
            SinhVien sv2 = new SinhVien(
                "SV001", 
                "nguyễn văn a", 
                new DateTime(2005, 5, 10), 
                9.5, 8.5, 9.0
            );
            sv2.HienThi();
            
            // 3. Tạo đối tượng với constructor 2 tham số
            Console.WriteLine("\n3. Constructor 2 tham số:");
            SinhVien sv3 = new SinhVien("SV002", "Trần Thị B");
            sv3.HienThi();
            
            // 4. Kiểm tra tính năng tự động tăng mã
            Console.WriteLine("\n4. Tự động tăng mã SV:");
            SinhVien sv4 = new SinhVien();
            Console.WriteLine($"Mã SV tự động: {sv4.MaSV}");
            
            // 5. Kiểm tra static property
            Console.WriteLine($"\n5. Tổng số sinh viên: {SinhVien.TongSo}");
            
            // 6. Kiểm tra validation
            Console.WriteLine("\n6. Kiểm tra validation:");
            try
            {
                SinhVien sv5 = new SinhVien("", "Hợp lệ", DateTime.Now, 9, 8, 7);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Lỗi: {ex.Message}");
            }
            
            try
            {
                SinhVien sv6 = new SinhVien("SV003", "Hợp lệ", 
                    DateTime.Now.AddDays(1), 9, 8, 7);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Lỗi: {ex.Message}");
            }
            
            try
            {
                SinhVien sv7 = new SinhVien("SV004", "Hợp lệ", 
                    new DateTime(2000, 1, 1), 11, 8, 7);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Lỗi: {ex.Message}");
            }
            
            // 7. Kiểm tra xếp loại
            Console.WriteLine("\n7. Kiểm tra xếp loại:");
            TestXepLoai(9.5, 9.0, 9.5); // Xuất sắc
            TestXepLoai(8.5, 8.0, 8.5); // Giỏi
            TestXepLoai(7.0, 7.5, 6.5); // Khá
            TestXepLoai(5.5, 5.0, 6.0); // Trung bình
            TestXepLoai(4.0, 4.5, 3.5); // Yếu
            
            // 8. Kiểm tra ToString()
            Console.WriteLine("\n8. Phương thức ToString():");
            Console.WriteLine(sv2.ToString());
            
            Console.WriteLine("\nNhấn phím bất kỳ để kết thúc...");
            Console.ReadKey();
        }
        
        static void TestXepLoai(double toan, double ly, double hoa)
        {
            SinhVien sv = new SinhVien("TEST", "Test", 
                new DateTime(2000, 1, 1), toan, ly, hoa);
            Console.WriteLine($"Điểm TB: {sv.DiemTrungBinh:F2} - Xếp loại: {sv.XepLoai}");
        }
    }
}

BƯỚC 5: CHẠY CHƯƠNG TRÌNH VÀ KIỂM TRA

5.1. Build Project

  • Nhấn Ctrl + Shift + B để biên dịch

  • Hoặc vào menu Build → Build Solution

5.2. Chạy chương trình

  • Nhấn F5 để chạy (Debug mode)

  • Hoặc Ctrl + F5 để chạy không debug

5.3. Kết quả dự kiến

===== KIỂM TRA LỚP SINH VIÊN =====
1. Constructor mặc định:
═══════════════════════════════════════
  Mã sinh viên  : SV001
  Họ và tên     : Chưa xác định
  Ngày sinh     : 01/01/2000
  Tuổi          : 24
  Điểm Toán     : 0.0
  Điểm Lý       : 0.0
  Điểm Hóa      : 0.0
  Điểm TB       : 0.00
  Xếp loại      : Yếu
═══════════════════════════════════════

2. Constructor có tham số:
═══════════════════════════════════════
  Mã sinh viên  : SV001
  Họ và tên     : Nguyễn Văn A
  Ngày sinh     : 10/05/2005
  Tuổi          : 19
  Điểm Toán     : 9.5
  Điểm Lý       : 8.5
  Điểm Hóa      : 9.0
  Điểm TB       : 9.00
  Xếp loại      : Xuất sắc
═══════════════════════════════════════

... (các kết quả khác)