Skip to content

Refleksi

Refleksi adalah mekanisme untuk memeriksa struktur bahasa itu sendiri saat runtime, ia dapat sangat fleksibel dalam menghadapi beberapa masalah, tetapi kerugian yang dibawa juga sangat jelas, misalnya masalah performa dll. Di Go, refleksi terkait erat dengan interface{}, sebagian besar, di mana ada interface{} muncul, akan ada refleksi. API refleksi di Go disediakan oleh paket standar reflect.

Interface

Sebelum memulai, mari pahami dua interface di paket runtime. Di Go, interface pada dasarnya adalah struct, Go pada runtime membagi interface menjadi dua kategori besar, satu adalah interface tanpa method set, yang lain adalah interface dengan method set. Untuk interface yang berisi method set, pada runtime direpresentasikan oleh struct iface sebagai berikut

go
type iface struct {
   tab  *itab // berisi tipe data, tipe interface, method set dll
   data unsafe.Pointer // pointer ke nilai
}

Sedangkan untuk interface tanpa method set, pada runtime direpresentasikan oleh struct eface, sebagai berikut

go
type eface struct {
   _type *_type // tipe
   data  unsafe.Pointer // pointer ke nilai
}

Kedua struct ini di paket reflect memiliki tipe struct yang sesuai, iface sesuai dengan nonEmptyInterface

go
type nonEmptyInterface struct {
  itab *struct {
    ityp *rtype // tipe interface statis
    typ  *rtype // tipe konkret dinamis
    hash uint32 // hash tipe
    _    [4]byte
    fun  [100000]unsafe.Pointer // method set
  }
  word unsafe.Pointer // pointer ke nilai
}

Sedangkan eface sesuai dengan emptyInterface

go
type emptyInterface struct {
   typ  *rtype // tipe konkret dinamis
   word unsafe.Pointer // nilai pointer
}

Untuk kedua tipe ini,官方 memberikan definisi yang sangat jelas

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

Di atas disebutkan istilah tipe konkret dinamis,原文 adalah dynamic concrete type, pertama Go adalah bahasa tipe statis seratus persen, statis ini tercermin dalam tipe interface abstrak yang ditampilkan ke luar adalah tetap, sedangkan dinamis表示 adalah tipe konkret yang disimpan di底层 interface dapat berubah. Sampai sini, untuk prinsip sederhana interface hanya perlu memahami ini sudah cukup untuk memenuhi pembelajaran refleksi selanjutnya.

Jembatan

Di paket reflect, masing-masing ada tipe interface reflect.Type untuk merepresentasikan tipe di Go, tipe struct reflect.Value untuk merepresentasikan nilai di Go

go
type Type interface {
    ...

    Name() string

  PkgPath() string

  Size() uintptr

  String() string

  Kind() Kind

    ...
}

type Value struct {

   typ *rtype

   ptr unsafe.Pointer

   flag

}

Kode di atas menghilangkan banyak detail, pertama hanya perlu memahami keberadaan kedua tipe ini. Semua operasi terkait refleksi di Go didasarkan pada kedua tipe ini, paket reflect menyediakan dua fungsi untuk mengkonversi tipe di Go menjadi kedua tipe di atas untuk melakukan operasi refleksi, masing-masing adalah fungsi reflect.TypeOf

go
func TypeOf(i any) Type

Dan fungsi reflect.ValueOf

go
func ValueOf(i any) Value

Dapat dilihat tipe parameter kedua fungsi adalah any, yaitu alias interface{}. Jika ingin melakukan operasi refleksi, perlu mengkonversi tipenya menjadi interface{} terlebih dahulu, ini juga mengapa di depan disebutkan只要有 refleksi tidak dapat lepas dari empty interface. Tidak ketat mengatakan, empty interface adalah jembatan yang menghubungkan sistem tipe Go dan refleksi, gambar di bawah ini sangat jelas menggambarkan prosesnya.

TIP

Di bawah ini untuk memudahkan, secara统一 menggunakan alias any untuk menggantikan interface{}

Inti

Di Go ada tiga hukum refleksi klasik, dikombinasikan dengan konten yang dibahas di atas sangat mudah dipahami, masing-masing sebagai berikut

  1. Refleksi dapat mengkonversi variabel tipe interface{} menjadi objek refleksi

  2. Refleksi dapat mengembalikan objek refleksi menjadi variabel tipe interface{}

  3. Untuk memodifikasi objek refleksi, nilainya harus dapat diset

Ketiga hukum ini adalah inti refleksi Go, saat perlu mengakses informasi tipe terkait, perlu menggunakan reflect.TypeOf, saat perlu memodifikasi nilai refleksi, perlu menggunakan reflect.ValueOf

Tipe

reflect.Type merepresentasikan tipe di Go, menggunakan fungsi reflect.TypeOf() dapat mengkonversi variabel menjadi reflect.Type. Contoh kode sebagai berikut

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

Hasil output

string

Kind

Untuk Type, Go internal menggunakan reflect.Kind untuk merepresentasikan tipe dasar di Go, pada dasarnya adalah unsigned integer uint.

go
type Kind uint

Paket reflect menggunakan Kind untuk mengenumerasi semua tipe dasar di Go, sebagai berikut

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
)

Tipe Kind hanya mengimplementasikan method String() dari interface Stringer, tipe ini juga hanya memiliki satu method ini, nilai return method String() berasal dari slice internalnya, sebagai berikut, penulisan ini sekilas sangat mirip map, tetapi sebenarnya ini adalah penulisan khusus di Go: syntax inisialisasi index (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
}

Melalui Kind, dapat mengetahui nilai yang disimpan empty interface sebenarnya adalah tipe dasar apa, misalnya

go
func main() {
    // Deklarasikan variabel tipe any
  var eface any
    // Assign nilai
  eface = 100
    // Melalui method Kind, untuk mendapatkan tipenya
  fmt.Println(reflect.TypeOf(eface).Kind())
}

Hasil output

int

Elem

go
type Type interface{
    Elem() Type
}

Menggunakan method Type.Elem(), dapat判断 tipe elemen yang disimpan oleh struktur data dengan tipe any, tipe parameter底层 yang dapat diterima harus salah satu dari pointer, slice, array, channel, map, jika tidak akan panic. Berikut adalah contoh kode

go
func main() {
  var eface any
  eface = map[string]int{}
  rType := reflect.TypeOf(eface)
    // key() akan mengembalikan tipe refleksi key map
  fmt.Println(rType.Key().Kind())
  fmt.Println(rType.Elem().Kind())
}

Output adalah

string
int

Pointer juga dapat dipahami sebagai container, untuk pointer menggunakan Elem() akan mendapatkan tipe refleksi elemen yang ditunjuknya, contoh kode sebagai berikut

go
func main() {
  var eface any
    // Assign pointer
  eface = new(strings.Builder)
  rType := reflect.TypeOf(eface)
    // Mendapatkan tipe refleksi elemen yang ditunjuk pointer
  vType := rType.Elem()
    // Output path paket
  fmt.Println(vType.PkgPath())
    // Output namanya
  fmt.Println(vType.Name())
}
strings
Builder

Untuk array, slice, channel penggunaannya sama.

Size

go
type Type interface{
    Size() uintptr
}

Melalui method Size dapat mendapatkan ukuran byte yang ditempati tipe yang sesuai, contoh sebagai berikut

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())
}

Hasil output adalah

8
16
16
8
24

TIP

Menggunakan unsafe.Sizeof() dapat mencapai efek yang sama

Comparable

go
type Type interface{
    Comparable() bool
}

Melalui method Comparable dapat判断 apakah suatu tipe dapat dibandingkan, contoh sebagai berikut

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())
}

Output sebagai berikut

true
true
false
true

Implements

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

Melalui method Implements dapat判断 apakah suatu tipe mengimplementasi interface tertentu

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))
}

Hasil output

true
false

ConvertibleTo

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

Menggunakan method ConvertibleTo dapat判断 apakah suatu tipe dapat dikonversi menjadi tipe target yang ditentukan

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))
}

Output

true
false

Nilai

reflect.Value merepresentasikan nilai interface refleksi, menggunakan fungsi reflect.ValueOf() dapat mengkonversi variabel menjadi reflect.Value. Contoh kode sebagai berikut

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

Hasil output

hello world!

Type

go
func (v Value) Type() Type

Method Type dapat mendapatkan tipe dari nilai refleksi

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

Output

int

Elem

go
func (v Value) Elem() Value

Mendapatkan nilai refleksi elemen dari nilai refleksi

go
func main() {
   num := new(int)
   *num = 114514
   // Dengan pointer sebagai contoh
   rValue := reflect.ValueOf(num).Elem()
   fmt.Println(rValue.Interface())
}

Output

114514

Pointer

Ada dua cara untuk mendapatkan pointer dari nilai refleksi

go
// Mengembalikan nilai refleksi pointer yang merepresentasikan alamat v
func (v Value) Addr() Value

// Mengembalikan uintptr ke nilai asli v setara dengan uintptr(Value.Addr().UnsafePointer())
func (v Value) UnsafeAddr() uintptr

// Mengembalikan uintptr ke nilai asli v
// Hanya ketika Kind v adalah Chan, Func, Map, Pointer, Slice, UnsafePointer, jika tidak akan panic
func (v Value) Pointer() uintptr

// Mengembalikan unsafe.Pointer ke nilai asli v
// Hanya ketika Kind v adalah Chan, Func, Map, Pointer, Slice, UnsafePointer, jika tidak akan panic
func (v Value) UnsafePointer() unsafe.Pointer

Contoh sebagai berikut

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())
}

Output

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

TIP

fmt.Println akan refleksi mendapatkan tipe parameter, jika tipe reflect.Value, akan otomatis memanggil Value.Interface() untuk mendapatkan nilai aslinya.

Ganti dengan map dan coba lagi

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())
}

Output

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

Set Nilai

go
func (v Value) Set(x Value)

Jika ingin memodifikasi nilai refleksi melalui refleksi, maka nilainya harus dapat di-address, saat ini harus melalui pointer untuk memodifikasi nilai elemennya, bukan langsung mencoba memodifikasi nilai elemen.

go
func main() {
   // *int
   num := new(int)
   *num = 114514
   rValue := reflect.ValueOf(num)
    // Mendapatkan elemen yang ditunjuk pointer
   ele := rValue.Elem()
   fmt.Println(ele.Interface())
   ele.SetInt(11)
   fmt.Println(ele.Interface())
}

Output sebagai berikut

114514
11

Mendapatkan Nilai

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

Melalui method Interface() dapat mendapatkan nilai asli dari nilai refleksi

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

Output

hello

Fungsi

Melalui refleksi dapat mendapatkan semua informasi fungsi, juga dapat memanggil fungsi melalui refleksi

Informasi

Melalui tipe refleksi untuk mendapatkan semua informasi fungsi

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

func main() {
   rType := reflect.TypeOf(Max)
   // Output nama fungsi, tipe fungsi literal tidak memiliki nama
   fmt.Println(rType.Name())
   // Output jumlah parameter, nilai return
   fmt.Println(rType.NumIn(), rType.NumOut())
   rParamType := rType.In(0)
   // Output tipe parameter pertama
   fmt.Println(rParamType.Kind())
   rResType := rType.Out(0)
   // Output tipe nilai return pertama
   fmt.Println(rResType.Kind())
}

Output


2 1
int
int

Memanggil

Melalui nilai refleksi untuk memanggil fungsi

go
func (v Value) Call(in []Value) []Value
go
func main() {
   // Mendapatkan nilai refleksi fungsi
   rType := reflect.ValueOf(Max)
   //传入 array parameter
   rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
   for _, value := range rResValue {
      fmt.Println(value.Interface())
   }
}

Output

50

Struct

Misalkan ada struct sebagai berikut

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
}

Mengakses Field

Struktur reflect.StructField sebagai berikut

go
type StructField struct {
  // Nama field
  Name string
  // Nama paket
  PkgPath string
  // Nama tipe
  Type      Type
  // Tag
  Tag       StructTag
  // Offset byte field
  Offset    uintptr
  // Index
  Index     []int
  // Apakah field embedded
  Anonymous bool
}

Ada dua cara untuk mengakses field struct, satu melalui index, yang lain melalui nama.

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

Contoh mengakses melalui index sebagai berikut

go
func main() {
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output jumlah field 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())
  }
}

Output

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)
}

Contoh mengakses melalui nama sebagai berikut

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   // Output jumlah field struct
   fmt.Println(rType.NumField())
   if field, ok := rType.FieldByName("money"); ok {
      fmt.Println(field.Name, field.Type, field.IsExported())
   }
}

Output

4
money int false

Memodifikasi Field

Jika ingin memodifikasi nilai field struct, harus传入 pointer struct, berikut adalah contoh memodifikasi field

go
func main() {
  //传入 pointer
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Mendapatkan field
  name := rValue.FieldByName("Name")
  // Memodifikasi nilai field
  if (name != reflect.Value{}) { // Jika mengembalikan reflect.Value{}, berarti field tidak ada
    name.SetString("jack")
  }
  // Output struct
  fmt.Println(rValue.Interface())
}

Output

{jack 0  0}

Untuk memodifikasi field private struct, perlu melakukan beberapa operasi tambahan, sebagai berikut

go
func main() {
  //传入 pointer
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Mendapatkan field private
  money := rValue.FieldByName("money")
  // Memodifikasi nilai field
  if (money != reflect.Value{}) {
    // Membangun nilai refleksi pointer ke field yang tidak diekspor struct ini
    p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
    // Mendapatkan elemen yang ditunjuk pointer ini, yaitu field yang akan dimodifikasi
    field := p.Elem()
    // Memodifikasi nilai
    field.SetInt(164)
  }
  // Output struct
  fmt.Printf("%+v\n", rValue.Interface())
}

Mengakses Tag

Setelah mendapatkan StructField, dapat langsung mengakses Tag-nya

go
// Jika tidak ada, ok adalah false
func (tag StructTag) Lookup(key string) (value string, ok bool)

// Jika tidak ada, mengembalikan string kosong
func (tag StructTag) Get(key string) string

Contoh sebagai berikut

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"))
   }
}

Output

name true
name

Mengakses Method

Mengakses method sangat mirip dengan proses mengakses field, hanya signature fungsi sedikit berbeda. Struktur reflect.Method sebagai berikut

go
type Method struct {
  // Nama method
  Name string
  // Nama paket
  PkgPath string
  // Tipe method
  Type  Type
  // Fungsi yang sesuai method, parameter pertama adalah receiver
  Func  Value
  // Index
  Index int
}

Contoh mengakses informasi method sebagai berikut

go
func main() {
  // Mendapatkan tipe refleksi struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output jumlah method
  fmt.Println(rType.NumMethod())
  // Traversing output informasi method
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
  }
}

Output

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

Jika ingin mendapatkan detail parameter dan nilai return method, dapat melalui Method.Func untuk mendapatkan, prosesnya sama dengan mengakses informasi fungsi, modifikasi sedikit kode di atas

go
func main() {
  // Mendapatkan tipe refleksi struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output jumlah method
  fmt.Println(rType.NumMethod())
  // Traversing output informasi method
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
    fmt.Println("Parameter method")
    for i := 0; i < method.Func.Type().NumIn(); i++ {
      fmt.Println(method.Func.Type().In(i).String())
    }
    fmt.Println("Nilai return method")
    for i := 0; i < method.Func.Type().NumOut(); i++ {
      fmt.Println(method.Func.Type().Out(i).String())
    }
  }
}

Dapat dilihat parameter pertama adalah main.Person, yaitu tipe receiver

1
0 Talk func(main.Person, string) string true
Parameter method
main.Person
string
Nilai return method
string

Memanggil Method

Memanggil method sangat mirip dengan proses memanggil fungsi, dan tidak perlu传入 receiver secara manual, contoh sebagai berikut

go
func main() {
   // Mendapatkan tipe refleksi struct
   rValue := reflect.ValueOf(new(Person)).Elem()
   // Output jumlah method
   fmt.Println(rValue.NumMethod())
   // Traversing output informasi method
   talk := rValue.MethodByName("Talk")
   if (talk != reflect.Value{}) {
      // Memanggil method, dan mendapatkan nilai return
      res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
      // Traversing output nilai return
      for _, re := range res {
         fmt.Println(re.Interface())
      }
   }
}

Output

1
hello,reflect!

Membuat

Melalui refleksi dapat membangun nilai baru, paket reflect sekaligus menyediakan fungsi yang lebih mudah digunakan berdasarkan beberapa tipe khusus.

Tipe Dasar

go
// Mengembalikan nilai refleksi pointer ke nilai refleksi
func New(typ Type) Value

Dengan string sebagai contoh

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

Struct

Pembuatan struct juga menggunakan fungsi 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() {
   // Membuat nilai refleksi struct
   rType := reflect.TypeOf(new(Person)).Elem()
   person := reflect.New(rType).Elem()
   fmt.Println(person.Interface())
}

Output

{ 0  0}

Slice

Refleksi membuat slice

go
func MakeSlice(typ Type, len, cap int) Value
go
func main() {
   // Membuat nilai refleksi slice
   rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
   // Traversing assign nilai
   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

Refleksi membuat Map

go
func MakeMapWithSize(typ Type, n int) Value
go
func main() {
   //Membangun nilai refleksi map
   rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
   // Set nilai
   rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
   fmt.Println(rValue.Interface())
}
map[a:1]

Channel

Refleksi membuat channel

go
func MakeChan(typ Type, buffer int) Value
go
func main() {
   // Membuat nilai refleksi channel
   makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
   fmt.Println(makeChan.Interface())
}

Fungsi

Refleksi membuat fungsi

go
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
go
func main() {
    //传入 tipe wrapper dan body fungsi
  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)})
}

Output

func(int)
1024

Sama Sepenuhnya

reflect.DeepEqual adalah fungsi yang disediakan di bawah paket refleksi untuk判断 apakah dua variabel sama sepenuhnya, signature sebagai berikut.

go
func DeepEqual(x, y any) bool

Fungsi ini melakukan penanganan untuk setiap tipe dasar, berikut adalah beberapa cara判断 tipe.

  • Array: setiap elemen di array sama sepenuhnya
  • Slice: keduanya nil, dianggap sama sepenuhnya, atau keduanya tidak kosong, elemen dalam rentang panjang sama sepenuhnya
  • Struct: semua field sama sepenuhnya
  • Map: keduanya nil, adalah sama sepenuhnya, keduanya tidak nil, nilai yang dipetakan setiap key sama sepenuhnya
  • Pointer: menunjuk elemen yang sama atau elemen yang ditunjuk sama sepenuhnya
  • Interface: saat tipe konkret interface sama sepenuhnya
  • Fungsi: hanya ketika keduanya nil adalah sama sepenuhnya, jika tidak bukan sama sepenuhnya

Berikut adalah beberapa contoh:

Slice

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

Output

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))
}

Output

false
false
true

Golang by www.golangdev.cn edit