Menu
CoddyTech

Cơ bản về reflection

Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 83 trên 107.

Phản chiếu (reflection) cho phép chương trình kiểm tra và thao tác với cấu trúc của chính nó khi chạy. Gói reflect của Go cung cấp khả năng này, giúp bạn kiểm tra kiểu dữ liệu, đọc các trường của struct và gọi phương thức một cách linh hoạt.

Hai kiểu cốt lõi trong reflection là reflect.Type và reflect.Value. Bạn lấy chúng bằng cách dùng reflect.TypeOf() và reflect.ValueOf():

import "reflect"

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}

t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

fmt.Println(t.Name())       // Person
fmt.Println(t.Kind())       // struct
fmt.Println(v.NumField())   // 2

Bạn có thể lặp qua các trường của struct để xem tên, kiểu dữ liệu và giá trị của chúng:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// Đầu ra:
// Name: Alice
// Age: 30

Reflection rất mạnh mẽ nhưng cũng có những đánh đổi: bỏ qua việc kiểm tra kiểu dữ liệu khi biên dịch, chạy chậm hơn so với truy cập trực tiếp và khiến code khó hiểu hơn. Chỉ dùng reflection khi cần cho các tác vụ như chuyển đổi dữ liệu JSON, ánh xạ ORM hoặc xây dựng các tiện ích tổng quát khi chưa biết kiểu dữ liệu lúc biên dịch.

challenge icon

Thử thách

Dễ

Hãy xây dựng một công cụ kiểm tra struct, sử dụng reflection để xem xét mọi struct và báo cáo cấu trúc của nó! Đây là một trường hợp sử dụng thực tế của reflection: tạo các tiện ích có thể hoạt động với những kiểu dữ liệu chưa biết khi biên dịch.

Bạn sẽ tổ chức code trong hai tệp:

  • inspector.go: Tạo tiện ích kiểm tra dựa trên reflection.

    Xây dựng một hàm InspectStruct(v any) string sử dụng gói reflect để xem xét mọi struct được truyền vào. Hàm cần trả về một báo cáo được định dạng, bao gồm:

    • Tên kiểu của struct
    • Loại của struct (phải là "struct")
    • Số lượng trường
    • Với mỗi trường: tên trường, kiểu và giá trị hiện tại

    Định dạng đầu ra như sau:

    Type: [TypeName]
    Kind: struct
    Fields: [count]
    - [FieldName] ([FieldType]): [Value]
    - [FieldName] ([FieldType]): [Value]
    ...

    Dùng reflect.TypeOf() để lấy thông tin kiểu và reflect.ValueOf() để truy cập giá trị các trường. Duyệt qua các trường bằng NumField(), dùng Field(i) trên kiểu để lấy siêu dữ liệu và dùng Field(i) trên giá trị để lấy các giá trị thực tế.

  • main.go: Định nghĩa các struct mẫu và dùng công cụ kiểm tra.

    Đọc kiểu struct (product hoặc employee) cùng các giá trị trường, sau đó tạo struct tương ứng và kiểm tra nó.

    Với product: Tạo struct Product có các trường Name (string), Price (float64) và InStock (bool). Đọc tên, giá và trạng thái còn hàng.

    Với employee: Tạo struct Employee có các trường ID (int), Name (string) và Department (string). Đọc ID, tên và phòng ban.

    Truyền struct đã tạo vào InspectStruct và in kết quả.

Đầu vào sẽ có dạng sau:

  • Dòng 1: Kiểu struct (product hoặc employee)
  • Các dòng tiếp theo: Giá trị các trường của kiểu struct đó

Ví dụ, với đầu vào:

product
Laptop
999.99
true

Đầu ra cần là:

Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): true

Và với đầu vào:

employee
42
Alice Johnson
Engineering

Đầu ra cần là:

Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineering

Và với đầu vào:

product
Headphones
79.50
false

Đầu ra cần là:

Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): false

Hãy chú ý cách công cụ kiểm tra hoạt động với nhiều kiểu struct khác nhau mà không cần biết trước cấu trúc của chúng. Đó là sức mạnh của reflection! Cùng một hàm InspectStruct có thể xử lý linh hoạt cả struct Product lẫn Employee.

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// Struct Product có các trường Name, Price và InStock
type Product struct {
	Name    string
	Price   float64
	InStock bool
}

// Struct Employee có các trường ID, Name và Department
type Employee struct {
	ID         int
	Name       string
	Department string
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Đọc kiểu struct
	structType, _ := reader.ReadString('\n')
	structType = strings.TrimSpace(structType)

	if structType == "product" {
		// Đọc các trường của sản phẩm
		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		priceStr, _ := reader.ReadString('\n')
		priceStr = strings.TrimSpace(priceStr)
		price, _ := strconv.ParseFloat(priceStr, 64)

		inStockStr, _ := reader.ReadString('\n')
		inStockStr = strings.TrimSpace(inStockStr)
		inStock, _ := strconv.ParseBool(inStockStr)

		// TODO: Tạo struct Product bằng các giá trị đã đọc
		// TODO: Gọi InspectStruct với sản phẩm và in ra kết quả

	} else if structType == "employee" {
		// Đọc các trường của nhân viên
		idStr, _ := reader.ReadString('\n')
		idStr = strings.TrimSpace(idStr)
		id, _ := strconv.Atoi(idStr)

		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		department, _ := reader.ReadString('\n')
		department = strings.TrimSpace(department)

		// TODO: Tạo struct Employee bằng các giá trị đã đọc
		// TODO: Gọi InspectStruct với nhân viên và in ra kết quả

	}
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Go online