Skip to content

Reflection

Reflection là một cơ chế kiểm tra cấu trúc của chính ngôn ngữ trong runtime nó có thể linh hoạt đối phó với một số vấn đề nhưng đồng thời cũng mang lại những bất lợi rõ ràng ví dụ như vấn đề hiệu suất v.v. Trong Go reflection liên quan chặt chẽ đến interface{} ở mức độ lớn bất cứ đâu có interface{} xuất hiện sẽ có reflection. API reflection trong Go do gói thư viện chuẩn reflect cung cấp.

Interface

Trước khi bắt đầu hãy tìm hiểu đơn giản về hai interface nằm trong gói runtime. Trong Go bản chất interface là struct Go chia interface thành hai loại lớn trong runtime một loại là interface không có method set loại kia là interface có method set. Đối với interface có method set trong runtime được biểu thị bằng struct iface như sau

go
type iface struct {
   tab  *itab // chứa loại dữ liệu loại interface method set v.v.
   data unsafe.Pointer // con trỏ trỏ đến giá trị
}

Còn đối với interface không có method set trong runtime được biểu thị bằng struct eface như sau

go
type eface struct {
   _type *_type // loại
   data  unsafe.Pointer // con trỏ trỏ đến giá trị
}

Và hai struct này trong gói reflect đều có loại struct tương ứng với chúng iface tương ứng với nonEmptyInterface

go
type nonEmptyInterface struct {
  itab *struct {
    ityp *rtype // loại interface tĩnh
    typ  *rtype // loại cụ thể động
    hash uint32 // hash loại
    _    [4]byte
    fun  [100000]unsafe.Pointer // method set
  }
  word unsafe.Pointer // con trỏ trỏ đến giá trị
}

Còn eface tương ứng với emptyInterface

go
type emptyInterface struct {
   typ  *rtype // loại cụ thể động
   word unsafe.Pointer // giá trị trỏ đến con trỏ
}

Đối với hai loại này chính thức đã đưa ra định nghĩa rất rõ ràng

  • nonEmptyInterface: nonEmptyInterface is the header for an interface value with methods
  • emptyInterface: emptyInterface is the header for an interface{} value

Ở trên đã đề cập đến cụm từ loại cụ thể động nguyên văn là dynamic concrete type trước hết Go là ngôn ngữ loại tĩnh 100% từ tĩnh thể hiện ở chỗ loại interface trừu tượng biểu hiện ra bên ngoài là không đổi còn động biểu thị là loại cụ thể được lưu trữ ở tầng dưới của interface có thể thay đổi. Đến đây đối với nguyên lý đơn giản của interface chỉ cần hiểu đến đây là đủ để đáp ứng việc học reflection sau này.

Cầu nối

Trong gói reflect lần lượt có loại interface reflect.Type để biểu thị loại trong Go loại struct reflect.Value để biểu thị giá trị trong Go

go
type Type interface {
    ...

    Name() string

  PkgPath() string

  Size() uintptr

  String() string

  Kind() Kind

    ...
}

type Value struct {

   typ *rtype

   ptr unsafe.Pointer

   flag

}

Mã trên đã bỏ qua nhiều chi tiết trước chỉ cần biết sự tồn tại của hai loại này là đủ. Tất cả các thao tác liên quan đến reflection trong Go đều dựa trên hai loại này gói reflect cung cấp hai hàm để chuyển đổi loại trong Go thành hai loại trên để thực hiện thao tác reflection lần lượt là hàm reflect.TypeOf

go
func TypeOf(i any) Type

Và hàm reflect.ValueOf

go
func ValueOf(i any) Value

Có thể thấy loại tham số của hai hàm đều là any tức là bí danh của interface{}. Nếu muốn thực hiện thao tác reflection cần chuyển đổi loại của nó thành interface{} trước đây cũng là lý do tại sao ở trên đã đề cập chỉ cần có reflection thì không thể rời khỏi interface rỗng. Nói một cách không chính xác interface rỗng chính là cầu nối kết nối hệ thống loại của Go và reflection hình dưới đây mô tả sinh động quá trình này.

TIP

Trong phần sau để thuận tiện thống nhất sử dụng bí danh any để thay thế interface{}

Cốt lõi

Trong Go có ba định luật reflection kinh điển kết hợp với nội dung đã nói ở trên sẽ rất dễ hiểu lần lượt như sau

  1. Reflection có thể chuyển đổi biến loại interface{} thành đối tượng reflection

  2. Reflection có thể khôi phục đối tượng reflection thành biến loại interface{}

  3. Muốn sửa đổi đối tượng reflection giá trị của nó phải có thể thiết lập được

Ba định luật này chính là cốt lõi của reflection trong Go khi cần truy cập thông tin loại liên quan thì cần dùng đến reflect.TypeOf khi cần sửa đổi giá trị reflection thì cần dùng đến reflect.ValueOf

Loại

reflect.Type đại diện cho loại trong Go sử dụng hàm reflect.TypeOf() có thể chuyển đổi biến thành reflect.Type. Ví dụ mã như sau

go
func main() {
  str := "hello world!"
  reflectType := reflect.TypeOf(str)
  fmt.Println(reflectType)
}

Kết quả xuất là

string

Kind

Đối với Type mà nói Go sử dụng reflect.Kind để biểu thị loại cơ sở trong Go bản chất của nó là số nguyên không dấu uint.

go
type Kind uint

Gói reflect sử dụng Kind để liệt kê tất cả các loại cơ sở trong Go như sau

go
const (
   Invalid Kind = iota
   Bool
   Int
   Int8
   Int16
   Int32
   Int64
   Uint
   Uint8
   Uint16
   Uint32
   Uint64
   Uintptr
   Float32
   Float64
   Complex64
   Complex128
   Array
   Chan
   Func
   Interface
   Map
   Pointer
   Slice
   String
   Struct
   UnsafePointer
)

Loại Kind chỉ thực hiện phương pháp String() của interface Stringer loại này cũng chỉ có một phương pháp này giá trị trả về của phương pháp String() đến từ một slice bên trong của nó như sau cách viết này nhìn qua rất giống map nhưng thực ra đây là một cách viết đặc biệt trong Go cú pháp khởi tạo chỉ mục (index expressions in slice literals)

go
var kindNames = []string{
   Invalid:       "invalid",
   Bool:          "bool",
   Int:           "int",
   Int8:          "int8",
   Int16:         "int16",
   Int32:         "int32",
   Int64:         "int64",
   Uint:          "uint",
   Uint8:         "uint8",
   Uint16:        "uint16",
   Uint32:        "uint32",
   Uint64:        "uint64",
   Uintptr:       "uintptr",
   Float32:       "float32",
   Float64:       "float64",
   Complex64:     "complex64",
   Complex128:    "complex128",
   Array:         "array",
   Chan:          "chan",
   Func:          "func",
   Interface:     "interface",
   Map:           "map",
   Pointer:       "ptr",
   Slice:         "slice",
   String:        "string",
   Struct:        "struct",
   UnsafePointer: "unsafe.Pointer",
}
go
type Type interface{
    Kind() Kind
}

Thông qua Kind có thể biết giá trị được lưu trữ trong interface rỗng rốt cuộc là loại cơ sở gì ví dụ

go
func main() {
    // Khai báo một biến loại any
  var eface any
    // Gán giá trị
  eface = 100
    // Thông qua phương pháp Kind để lấy loại của nó
  fmt.Println(reflect.TypeOf(eface).Kind())
}

Kết quả xuất

int

Elem

go
type Type interface{
    Elem() Type
}

Sử dụng phương pháp Type.Elem() có thể kiểm tra loại phần tử được lưu trữ trong cấu trúc dữ liệu loại any loại tham số tầng dưới có thể nhận phải là một trong các loại con trỏ slice mảng channel map nếu không sẽ panic. Dưới đây là ví dụ mã

go
func main() {
  var eface any
  eface = map[string]int{}
  rType := reflect.TypeOf(eface)
    // key() sẽ trả về loại reflection của key map
  fmt.Println(rType.Key().Kind())
  fmt.Println(rType.Elem().Kind())
}

Xuất là

string
int

Con trỏ cũng có thể hiểu là một container đối với con trỏ sử dụng Elem() sẽ nhận được loại reflection của phần tử mà nó trỏ đến ví dụ mã như sau

go
func main() {
  var eface any
    // Gán giá trị con trỏ
  eface = new(strings.Builder)
  rType := reflect.TypeOf(eface)
    // Lấy loại reflection của phần tử mà con trỏ trỏ đến
  vType := rType.Elem()
    // Xuất đường dẫn gói
  fmt.Println(vType.PkgPath())
    // Xuất tên của nó
  fmt.Println(vType.Name())
}
strings
Builder

Đối với mảng slice channel sử dụng đều tương tự.

Size

go
type Type interface{
    Size() uintptr
}

Thông qua phương pháp Size có thể lấy được kích thước byte mà loại tương ứng chiếm ví dụ như sau

go
func main() {
  fmt.Println(reflect.TypeOf(0).Size())
  fmt.Println(reflect.TypeOf("").Size())
  fmt.Println(reflect.TypeOf(complex(0, 0)).Size())
  fmt.Println(reflect.TypeOf(0.1).Size())
  fmt.Println(reflect.TypeOf([]string{}).Size())
}

Kết quả xuất là

8
16
16
8
24

TIP

Sử dụng unsafe.Sizeof() có thể đạt được hiệu quả tương tự

Comparable

go
type Type interface{
    Comparable() bool
}

Thông qua phương pháp Comparable có thể kiểm tra một loại có thể được so sánh hay không ví dụ như sau

go
func main() {
  fmt.Println(reflect.TypeOf("hello world!").Comparable())
  fmt.Println(reflect.TypeOf(1024).Comparable())
  fmt.Println(reflect.TypeOf([]int{}).Comparable())
  fmt.Println(reflect.TypeOf(struct{}{}).Comparable())
}

Xuất như sau

true
true
false
true

Implements

go
type Type interface{
    Implements(u Type) bool
}

Thông qua phương pháp Implements có thể kiểm tra một loại có thực hiện một interface nào đó hay không

go
type MyInterface interface {
  My() string
}

type MyStruct struct {
}

func (m MyStruct) My() string {
  return "my"
}

type HisStruct struct {
}

func (h HisStruct) String() string {
  return "his"
}

func main() {
  rIface := reflect.TypeOf(new(MyInterface)).Elem()
  fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().Implements(rIface))
  fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().Implements(rIface))
}

Kết quả xuất

true
false

ConvertibleTo

go
type Type interface{
    ConvertibleTo(u Type) bool
}

Sử dụng phương pháp ConvertibleTo có thể kiểm tra một loại có thể được chuyển đổi thành một loại chỉ định khác hay không

go
type MyInterface interface {
  My() string
}

type MyStruct struct {
}

func (m MyStruct) My() string {
  return "my"
}

type HisStruct struct {
}

func (h HisStruct) String() string {
  return "his"
}

func main() {
  rIface := reflect.TypeOf(new(MyInterface)).Elem()
  fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().ConvertibleTo(rIface))
  fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().ConvertibleTo(rIface))
}

Xuất

true
false

Giá trị

reflect.Value đại diện cho giá trị của interface reflection sử dụng hàm reflect.ValueOf() có thể chuyển đổi biến thành reflect.Value. Ví dụ mã như sau

go
func main() {
  str := "hello world!"
  reflectValue := reflect.ValueOf(str)
  fmt.Println(reflectValue)
}

Kết quả xuất là

hello world!

Type

go
func (v Value) Type() Type

Phương pháp Type có thể lấy được loại của một giá trị reflection

go
func main() {
   num := 114514
   rValue := reflect.ValueOf(num)
   fmt.Println(rValue.Type())
}

Xuất

int

Elem

go
func (v Value) Elem() Value

Lấy giá trị reflection của phần tử của một giá trị reflection

go
func main() {
   num := new(int)
   *num = 114514
   // Lấy con trỏ làm ví dụ
   rValue := reflect.ValueOf(num).Elem()
   fmt.Println(rValue.Interface())
}

Xuất

114514

Con trỏ

Có hai cách để lấy con trỏ của một giá trị reflection

go
// Trả về giá trị reflection con trỏ biểu thị địa chỉ của v
func (v Value) Addr() Value

// Trả về một uinptr trỏ đến giá trị gốc của v tương đương với uintptr(Value.Addr().UnsafePointer())
func (v Value) UnsafeAddr() uintptr

// Trả về một uintptr trỏ đến giá trị gốc của v
// Chỉ khi Kind của v là Chan Func Map Pointer Slice UnsafePointer nếu không sẽ panic
func (v Value) Pointer() uintptr

// Trả về một unsafe.Pointer trỏ đến giá trị gốc của v
// Chỉ khi Kind của v là Chan Func Map Pointer Slice UnsafePointer nếu không sẽ panic
func (v Value) UnsafePointer() unsafe.Pointer

Ví dụ như sau

go
func main() {
   num := 1024
   ele := reflect.ValueOf(&num).Elem()
   fmt.Println("&num", &num)
   fmt.Println("Addr", ele.Addr())
   fmt.Println("UnsafeAddr", unsafe.Pointer(ele.UnsafeAddr()))
   fmt.Println("Pointer", unsafe.Pointer(ele.Addr().Pointer()))
   fmt.Println("UnsafePointer", ele.Addr().UnsafePointer())
}

Xuất

&num 0xc0000a6058
Addr 0xc0000a6058
UnsafeAddr 0xc0000a6058
Pointer 0xc0000a6058
UnsafePointer 0xc0000a6058

TIP

fmt.Println sẽ reflection lấy loại của tham số nếu là loại reflect.Value thì sẽ tự động gọi Value.Interface() để lấy giá trị gốc của nó.

Đổi thành một map và thực hiện lại

go
func main() {
  dic := map[string]int{}
  ele := reflect.ValueOf(&dic).Elem()
  println(dic)
  fmt.Println("Addr", ele.Addr())
  fmt.Println("UnsafeAddr", *(*unsafe.Pointer)(unsafe.Pointer(ele.UnsafeAddr())))
  fmt.Println("Pointer", unsafe.Pointer(ele.Pointer()))
  fmt.Println("UnsafePointer", ele.UnsafePointer())
}

Xuất

0xc00010e4b0
Addr &map[]
UnsafeAddr 0xc00010e4b0
Pointer 0xc00010e4b0
UnsafePointer 0xc00010e4b0

Thiết lập giá trị

go
func (v Value) Set(x Value)

Nếu thông qua reflection để sửa đổi giá trị reflection thì giá trị của nó phải có thể lấy địa chỉ lúc này nên thông qua con trỏ để sửa đổi giá trị phần tử của nó chứ không nên trực tiếp cố gắng sửa đổi giá trị của phần tử.

go
func main() {
   // *int
   num := new(int)
   *num = 114514
   rValue := reflect.ValueOf(num)
    // Lấy phần tử mà con trỏ trỏ đến
   ele := rValue.Elem()
   fmt.Println(ele.Interface())
   ele.SetInt(11)
   fmt.Println(ele.Interface())
}

Xuất như sau

114514
11

Lấy giá trị

go
func (v Value) Interface() (i any)

Thông qua phương pháp Interface() có thể lấy được giá trị gốc của giá trị reflection

go
func main() {
   var str string
   str = "hello"
   rValue := reflect.ValueOf(str)
   if v, ok := rValue.Interface().(string); ok {
      fmt.Println(v)
   }
}

Xuất

hello

Hàm

Thông qua reflection có thể lấy tất cả thông tin của hàm cũng có thể gọi hàm thông qua reflection

Thông tin

Thông qua loại reflection để lấy tất cả thông tin của hàm

go
func Max(a, b int) int {
   if a > b {
      return a
   }
   return b
}

func main() {
   rType := reflect.TypeOf(Max)
   // Xuất tên hàm loại hàm literal không có tên
   fmt.Println(rType.Name())
   // Xuất số lượng tham số giá trị trả về
   fmt.Println(rType.NumIn(), rType.NumOut())
   rParamType := rType.In(0)
   // Xuất loại của tham số đầu tiên
   fmt.Println(rParamType.Kind())
   rResType := rType.Out(0)
   // Xuất loại của giá trị trả về đầu tiên
   fmt.Println(rResType.Kind())
}

Xuất


2 1
int
int

Gọi

Thông qua giá trị reflection để gọi hàm

go
func (v Value) Call(in []Value) []Value
go
func main() {
   // Lấy giá trị reflection của hàm
   rType := reflect.ValueOf(Max)
   // Truyền vào mảng tham số
   rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
   for _, value := range rResValue {
      fmt.Println(value.Interface())
   }
}

Xuất

50

Struct

Giả sử có struct như sau

go
type Person struct {
  Name    string `json:"name"`
  Age     int    `json:"age"`
  Address string `json:"address"`
  money   int
}

func (p Person) Talk(msg string) string {
  return msg
}

Truy cập trường

Cấu trúc của reflect.StructField như sau

go
type StructField struct {
  // Tên trường
  Name string
  // Tên gói
  PkgPath string
  // Tên loại
  Type      Type
  // Tag
  Tag       StructTag
  // Độ lệch byte của trường
  Offset    uintptr
  // Chỉ mục
  Index     []int
  // Có phải là trường nhúng không
  Anonymous bool
}

Có hai phương pháp để truy cập trường của struct một là thông qua chỉ mục để truy cập hai là thông qua tên.

go
type Type interface{
    Field(i int) StructField
}

Ví dụ truy cập thông qua chỉ mục như sau

go
func main() {
  rType := reflect.TypeOf(new(Person)).Elem()
  // Xuất số lượng trường của struct
  fmt.Println(rType.NumField())
  for i := 0; i < rType.NumField(); i++ {
    structField := rType.Field(i)
    fmt.Println(structField.Index, structField.Name, structField.Type, structField.Offset, structField.IsExported())
  }
}

Xuất

4
[0] Name string 0 true
[1] Age int 16 true
[2] Address string 24 true
[3] money int 40 false
go
type Type interface{
    FieldByName(name string) (StructField, bool)
}

Ví dụ truy cập thông qua tên như sau

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   // Xuất số lượng trường của struct
   fmt.Println(rType.NumField())
   if field, ok := rType.FieldByName("money"); ok {
      fmt.Println(field.Name, field.Type, field.IsExported())
   }
}

Xuất

4
money int false

Sửa đổi trường

Nếu muốn sửa đổi giá trị trường của struct thì phải truyền vào một con trỏ struct dưới đây là một ví dụ sửa đổi trường

go
func main() {
  // Truyền vào con trỏ
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Lấy trường
  name := rValue.FieldByName("Name")
  // Sửa đổi giá trị trường
  if (name != reflect.Value{}) { // Nếu trả về reflect.Value{} thì biểu thị trường này không tồn tại
    name.SetString("jack")
  }
  // Xuất struct
  fmt.Println(rValue.Interface())
}

Xuất

{jack 0  0}

Đối với việc sửa đổi trường riêng tư của struct cần thực hiện một số thao tác bổ sung như sau

go
func main() {
  // Truyền vào con trỏ
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Lấy một trường riêng tư
  money := rValue.FieldByName("money")
  // Sửa đổi giá trị trường
  if (money != reflect.Value{}) {
    // Xây dựng giá trị reflection con trỏ trỏ đến trường chưa xuất của struct này
    p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
    // Lấy phần tử mà con trỏ này trỏ đến cũng chính là trường cần sửa đổi
    field := p.Elem()
    // Sửa đổi giá trị
    field.SetInt(164)
  }
  // Xuất struct
  fmt.Printf("%+v\n", rValue.Interface())
}

Truy cập Tag

Sau khi lấy được StructField thì có thể trực tiếp truy cập Tag của nó

go
// Nếu không tồn tại ok là false
func (tag StructTag) Lookup(key string) (value string, ok bool)

// Nếu không tồn tại trả về chuỗi rỗng
func (tag StructTag) Get(key string) string

Ví dụ như sau

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   name, ok := rType.FieldByName("Name")
   if ok {
      fmt.Println(name.Tag.Lookup("json"))
      fmt.Println(name.Tag.Get("json"))
   }
}

Xuất

name true
name

Truy cập phương pháp

Truy cập phương pháp rất giống với quá trình truy cập trường chỉ là chữ ký hàm có chút khác biệt. Struct reflect.Method như sau

go
type Method struct {
  // Tên phương pháp
  Name string
  // Tên gói
  PkgPath string
  // Loại phương pháp
  Type  Type
  // Hàm tương ứng với phương pháp tham số đầu tiên là receiver
  Func  Value
  // Chỉ mục
  Index int
}

Ví dụ truy cập thông tin phương pháp như sau

go
func main() {
  // Lấy loại reflection của struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Xuất số lượng phương pháp
  fmt.Println(rType.NumMethod())
  // Duyệt xuất thông tin phương pháp
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
  }
}

Xuất

1
0 Talk func(main.Person, string) string true

Nếu muốn lấy chi tiết tham số và giá trị trả về của phương pháp có thể thông qua Method.Func để lấy quá trình tương tự như truy cập thông tin hàm sửa đổi mã trên một chút

go
func main() {
  // Lấy loại reflection của struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Xuất số lượng phương pháp
  fmt.Println(rType.NumMethod())
  // Duyệt xuất thông tin phương pháp
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
    fmt.Println("Tham số phương pháp")
    for i := 0; i < method.Func.Type().NumIn(); i++ {
      fmt.Println(method.Func.Type().In(i).String())
    }
    fmt.Println("Giá trị trả về phương pháp")
    for i := 0; i < method.Func.Type().NumOut(); i++ {
      fmt.Println(method.Func.Type().Out(i).String())
    }
  }
}

Có thể thấy tham số đầu tiên là main.Person cũng chính là loại receiver

1
0 Talk func(main.Person, string) string true
Tham số phương pháp
main.Person
string
Giá trị trả về phương pháp
string

Gọi phương pháp

Gọi phương pháp tương tự như quá trình gọi hàm hơn nữa không cần thủ công truyền vào receiver ví dụ như sau

go
func main() {
   // Lấy loại reflection của struct
   rValue := reflect.ValueOf(new(Person)).Elem()
   // Xuất số lượng phương pháp
   fmt.Println(rValue.NumMethod())
   // Duyệt xuất thông tin phương pháp
   talk := rValue.MethodByName("Talk")
   if (talk != reflect.Value{}) {
      // Gọi phương pháp và lấy giá trị trả về
      res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
      // Duyệt xuất giá trị trả về
      for _, re := range res {
         fmt.Println(re.Interface())
      }
   }
}

Xuất

1
hello,reflect!

Tạo

Thông qua reflection có thể xây dựng giá trị mới gói reflect đồng thời cung cấp các hàm tiện lợi khác nhau dựa trên một số loại đặc biệt.

Loại cơ bản

go
// Trả về giá trị reflection con trỏ trỏ đến giá trị reflection
func New(typ Type) Value

Lấy string làm ví dụ

go
func main() {
   rValue := reflect.New(reflect.TypeOf(*new(string)))
   rValue.Elem().SetString("hello world!")
   fmt.Println(rValue.Elem().Interface())
}
hello world!

Struct

Việc tạo struct cũng sử dụng hàm reflect.New

go
type Person struct {
   Name    string `json:"name"`
   Age     int    `json:"age"`
   Address string `json:"address"`
   money   int
}

func (p Person) Talk(msg string) string {
   return msg
}

func main() {
   // Tạo giá trị reflection struct
   rType := reflect.TypeOf(new(Person)).Elem()
   person := reflect.New(rType).Elem()
   fmt.Println(person.Interface())
}

Xuất

{ 0  0}

Slice

Reflection tạo slice

go
func MakeSlice(typ Type, len, cap int) Value
go
func main() {
   // Tạo giá trị reflection slice
   rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
   // Duyệt gán giá trị
   for i := 0; i < 10; i++ {
      rValue.Index(i).SetInt(int64(i))
   }
   fmt.Println(rValue.Interface())
}
[0 1 2 3 4 5 6 7 8 9]

Map

Reflection tạo Map

go
func MakeMapWithSize(typ Type, n int) Value
go
func main() {
   // Xây dựng giá trị reflection map
   rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
   // Thiết lập giá trị
   rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
   fmt.Println(rValue.Interface())
}
map[a:1]

Channel

Reflection tạo channel

go
func MakeChan(typ Type, buffer int) Value
go
func main() {
   // Tạo giá trị reflection channel
   makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
   fmt.Println(makeChan.Interface())
}

Hàm

Reflection tạo hàm

go
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
go
func main() {
    // Truyền vào loại wrapper và thân hàm
  fn := reflect.MakeFunc(reflect.TypeOf(new(func(int))).Elem(), func(args []reflect.Value) (results []reflect.Value) {
    for _, arg := range args {
      fmt.Println(arg.Interface())
    }
    return nil
  })
  fmt.Println(fn.Type())
  fn.Call([]reflect.Value{reflect.ValueOf(1024)})
}

Xuất

func(int)
1024

Hoàn toàn bằng nhau

reflect.DeepEqual là một hàm do gói reflection cung cấp dùng để kiểm tra hai biến có hoàn toàn bằng nhau hay không chữ ký như sau.

go
func DeepEqual(x, y any) bool

Hàm này đã xử lý cho từng loại cơ sở dưới đây là một số cách kiểm tra loại.

  • Mảng mỗi phần tử trong mảng đều hoàn toàn bằng nhau
  • Slice đều là nil thì kiểm tra là hoàn toàn bằng nhau hoặc đều không rỗng thì các phần tử trong phạm vi độ dài hoàn toàn bằng nhau
  • Struct tất cả các trường đều hoàn toàn bằng nhau
  • Map đều là nil thì là hoàn toàn bằng nhau đều không phải nil thì giá trị mà mỗi key ánh xạ đều hoàn toàn bằng nhau
  • Con trỏ trỏ đến cùng một phần tử hoặc các phần tử trỏ đến hoàn toàn bằng nhau
  • Interface loại cụ thể của interface hoàn toàn bằng nhau
  • Hàm chỉ khi cả hai đều là nil mới là hoàn toàn bằng nhau nếu không thì không phải hoàn toàn bằng nhau

Dưới đây là một số ví dụ

Slice

go
func main() {
   a := make([]int, 100)
   b := make([]int, 100)
   fmt.Println(reflect.DeepEqual(a, b))
}

Xuất

true

Struct

go
func main() {
   mike := Person{
      Name:   "mike",
      Age:    39,
      Father: nil,
   }

   jack := Person{
      Name:   "jack",
      Age:    18,
      Father: &mike,
   }

   tom := Person{
      Name:   "tom",
      Age:    18,
      Father: &mike,
   }
   fmt.Println(reflect.DeepEqual(mike, jack))
   fmt.Println(reflect.DeepEqual(tom, jack))
   fmt.Println(reflect.DeepEqual(jack, jack))
}

Xuất

false
false
true

Golang by www.golangdev.cn edit