Bài tập 2(B9_C#):LỚP SẢN PHẨM

Sau khi hoàn thành bài tập này, bạn sẽ:

  1. ✅ Xây dựng lớp SanPham với các thuộc tính cơ bản

  2. ✅ Sử dụng static để tự động sinh mã sản phẩm

  3. ✅ Áp dụng validation cho dữ liệu

  4. ✅ Xây dựng phương thức nhập khoxuất kho

  5. ✅ Sử dụng kiểu decimal cho tiền tệ

  6. ✅ Tính toán giá trị tồn kho tự động

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

1.1. Mở Visual Studio

  • Mở Visual Studio 2022

File → New → Project

Hoặc nhấn: 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 Project

Project name: Bai2_SanPham
Location: D:\CSharp\LapTrinhHuongDoiTuong (hoặc thư mục của bạn)
Solution: LapTrinhHuongDoiTuong (chọn Add to Solution)
  • Nhấn Next

1.5. Chọn Framework

  • Chọn .NET 6.0 hoặc .NET 8.0 (LTS)

  • 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 Project Bai2_SanPham

  2. Chọn Add → New Folder

  3. Đặt tên: Models

  4. Nhấn Enter

BƯỚC 3: TẠO FILE LỚP SANPHAM

3.1. Tạo file SanPham.cs

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

  2. Chọn Add → Class

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

  4. Nhấn Add

BƯỚC 4: VIẾT CODE CHO LỚP SANPHAM

4.1. Khai báo Namespace và Class

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

using System;
namespace Bai2_SanPham.Models
{
    public class SanPham
    {
        // Code sẽ viết ở đây
    }
}

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

public class SanPham
{
    // === FIELDS (Trường dữ liệu) ===
    private string _maSP;          // Mã sản phẩm (tự sinh)
    private string _tenSP;         // Tên sản phẩm
    private decimal _donGia;       // Đơn giá (dùng decimal cho tiền tệ)
    private int _soLuongTon;       // Số lượng tồn kho
    private static int _soLuongSP = 0;  // Đếm số SP đã tạo (static)
}

Giải thích Fields:

FieldKiểuMục đíchLưu ý
_maSPstringLưu mã sản phẩmSẽ tự động sinh
_tenSPstringLưu tên sản phẩmKiểm tra không rỗng
_donGiadecimalLưu đơn giáDùng decimal cho tiền tệ
_soLuongTonintLưu số lượng tồnKhông được âm
_soLuongSPstatic intĐếm số SP đã tạoDùng chung cho tất cả SP

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

a. Property MaSP (Read-only – chỉ đọc)

public class SanPham
{
    // ... Fields ở trên ...

    // === PROPERTIES ===
    
    // 1. Mã sản phẩm - Chỉ đọc, tự sinh
    public string MaSP
    {
        get { return _maSP; }
        // Không có set → Không thể thay đổi từ bên ngoài
    }
}

b. Property TenSP (Có validation)

    // 2. Tên sản phẩm - Có kiểm tra không rỗng
    public string TenSP
    {
        get { return _tenSP; }
        set
        {
            // Kiểm tra tên không được để trống
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Tên sản phẩm không được để trống!");
            _tenSP = value.Trim();
        }
    }

c. Property DonGia (Có validation)

    // 3. Đơn giá - Không được âm
    public decimal DonGia
    {
        get { return _donGia; }
        set
        {
            if (value < 0)
                throw new ArgumentException("Đơn giá không được âm!");
            _donGia = value;
        }
    }

d. Property SoLuongTon (Có validation)

    // 4. Số lượng tồn - Không được âm
    public int SoLuongTon
    {
        get { return _soLuongTon; }
        set
        {
            if (value < 0)
                throw new ArgumentException("Số lượng tồn không được âm!");
            _soLuongTon = value;
        }
    }

e. Property GiaTriTon (Computed – tự tính)

    // 5. Giá trị tồn kho - Tự động tính (Read-only)
    public decimal GiaTriTon
    {
        get { return _soLuongTon * _donGia; }
    }

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

a. Constructor mặc định (Tự sinh mã SP)

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

    // === CONSTRUCTORS ===

    // Constructor 1: Không tham số - Tự sinh mã SP
    public SanPham()
    {
        // Tăng số lượng SP lên 1 trước
        _soLuongSP++;
        
        // Tự sinh mã SP: SP001, SP002, SP003, ...
        _maSP = "SP" + _soLuongSP.ToString("D3");
        
        // Gán giá trị mặc định
        _tenSP = "Chưa xác định";
        _donGia = 0;
        _soLuongTon = 0;
    }
}

Giải thích:

  • _soLuongSP++ – Tăng biến đếm lên 1

  • ToString("D3") – Định dạng số thành 3 chữ số: 001, 002, 003, …

  • _maSP = "SP" + "001" → “SP001”

b. Constructor có tham số

    // Constructor 2: Có tham số đầy đủ
    public SanPham(string tenSP, decimal donGia, int soLuongTon) : this()
    {
        // this() gọi constructor mặc định để tự sinh mã SP
        
        // Gán giá trị (có validation)
        TenSP = tenSP;
        DonGia = donGia;
        SoLuongTon = soLuongTon;
    }

Giải thích:

  • : this() – Gọi constructor mặc định trước (để tự sinh mã SP)

  • Sau đó gán các giá trị khác


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

a. Phương thức HienThi()

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

    // === METHODS ===

    // 1. Hiển thị thông tin sản phẩm
    public void HienThi()
    {
        Console.WriteLine("═══════════════════════════════════════");
        Console.WriteLine($"  Mã SP         : {_maSP}");
        Console.WriteLine($"  Tên SP        : {_tenSP}");
        Console.WriteLine($"  Đơn giá       : {_donGia:N0} VND");
        Console.WriteLine($"  SL Tồn        : {_soLuongTon}");
        Console.WriteLine($"  Giá trị tồn   : {GiaTriTon:N0} VND");
        Console.WriteLine("═══════════════════════════════════════");
    }
}

b. Phương thức NhapKho()

    // 2. Nhập kho (tăng số lượng tồn)
    public void NhapKho(int soLuong)
    {
        // Kiểm tra số lượng nhập phải > 0
        if (soLuong <= 0)
        {
            Console.WriteLine("❌ Số lượng nhập phải lớn hơn 0!");
            return;
        }
        
        // Tăng số lượng tồn
        _soLuongTon += soLuong;
        Console.WriteLine($"✓ Đã nhập kho {soLuong} sản phẩm. SL tồn: {_soLuongTon}");
    }

c. Phương thức XuatKho()

    // 3. Xuất kho (giảm số lượng tồn)
    public void XuatKho(int soLuong)
    {
        // Kiểm tra số lượng xuất phải > 0
        if (soLuong <= 0)
        {
            Console.WriteLine("❌ Số lượng xuất phải lớn hơn 0!");
            return;
        }
        
        // Kiểm tra số lượng tồn có đủ không
        if (soLuong > _soLuongTon)
        {
            Console.WriteLine($"❌ Không đủ hàng! Tồn: {_soLuongTon}, Yêu cầu: {soLuong}");
            return;
        }
        
        // Giảm số lượng tồn
        _soLuongTon -= soLuong;
        Console.WriteLine($"✓ Đã xuất kho {soLuong} sản phẩm. SL tồn: {_soLuongTon}");
    }

4.6. Code hoàn chỉnh của SanPham.cs

using System;

namespace Bai2_SanPham.Models
{
    public class SanPham
    {
        // === FIELDS ===
        private string _maSP;
        private string _tenSP;
        private decimal _donGia;
        private int _soLuongTon;
        private static int _soLuongSP = 0;

        // === PROPERTIES ===
        
        // 1. Mã sản phẩm - Read-only
        public string MaSP => _maSP;

        // 2. Tên sản phẩm - Có validation
        public string TenSP
        {
            get => _tenSP;
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new ArgumentException("Tên sản phẩm không được để trống!");
                _tenSP = value.Trim();
            }
        }

        // 3. Đơn giá - Không được âm
        public decimal DonGia
        {
            get => _donGia;
            set
            {
                if (value < 0)
                    throw new ArgumentException("Đơn giá không được âm!");
                _donGia = value;
            }
        }

        // 4. Số lượng tồn - Không được âm
        public int SoLuongTon
        {
            get => _soLuongTon;
            set
            {
                if (value < 0)
                    throw new ArgumentException("Số lượng tồn không được âm!");
                _soLuongTon = value;
            }
        }

        // 5. Giá trị tồn kho - Tự động tính
        public decimal GiaTriTon => _soLuongTon * _donGia;

        // === CONSTRUCTORS ===
        
        // Constructor mặc định - Tự sinh mã SP
        public SanPham()
        {
            _soLuongSP++;
            _maSP = "SP" + _soLuongSP.ToString("D3");
            _tenSP = "Chưa xác định";
            _donGia = 0;
            _soLuongTon = 0;
        }

        // Constructor có tham số
        public SanPham(string tenSP, decimal donGia, int soLuongTon) : this()
        {
            TenSP = tenSP;
            DonGia = donGia;
            SoLuongTon = soLuongTon;
        }

        // === METHODS ===
        
        // 1. Hiển thị thông tin
        public void HienThi()
        {
            Console.WriteLine("═══════════════════════════════════════");
            Console.WriteLine($"  Mã SP         : {_maSP}");
            Console.WriteLine($"  Tên SP        : {_tenSP}");
            Console.WriteLine($"  Đơn giá       : {_donGia:N0} VND");
            Console.WriteLine($"  SL Tồn        : {_soLuongTon}");
            Console.WriteLine($"  Giá trị tồn   : {GiaTriTon:N0} VND");
            Console.WriteLine("═══════════════════════════════════════");
        }

        // 2. Nhập kho
        public void NhapKho(int soLuong)
        {
            if (soLuong <= 0)
            {
                Console.WriteLine("❌ Số lượng nhập phải lớn hơn 0!");
                return;
            }
            _soLuongTon += soLuong;
            Console.WriteLine($"✓ Đã nhập kho {soLuong} sản phẩm. SL tồn: {_soLuongTon}");
        }

        // 3. Xuất kho
        public void XuatKho(int soLuong)
        {
            if (soLuong <= 0)
            {
                Console.WriteLine("❌ Số lượng xuất phải lớn hơn 0!");
                return;
            }
            if (soLuong > _soLuongTon)
            {
                Console.WriteLine($"❌ Không đủ hàng! Tồn: {_soLuongTon}, Yêu cầu: {soLuong}");
                return;
            }
            _soLuongTon -= soLuong;
            Console.WriteLine($"✓ Đã xuất kho {soLuong} sản phẩm. SL tồn: {_soLuongTon}");
        }
    }
}