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
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
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
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
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 methodsemptyInterface: 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
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
func TypeOf(i any) TypeVà hàm reflect.ValueOf
func ValueOf(i any) ValueCó 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
Reflection có thể chuyển đổi biến loại
interface{}thành đối tượng reflectionReflection có thể khôi phục đối tượng reflection thành biến loại
interface{}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
func main() {
str := "hello world!"
reflectType := reflect.TypeOf(str)
fmt.Println(reflectType)
}Kết quả xuất là
stringKind
Đố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.
type Kind uintGói reflect sử dụng Kind để liệt kê tất cả các loại cơ sở trong Go như sau
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)
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",
}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ụ
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
intElem
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ã
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
intCon 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
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
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
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
24TIP
Sử dụng unsafe.Sizeof() có thể đạt được hiệu quả tương tự
Comparable
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
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
trueImplements
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
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
falseConvertibleTo
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
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
falseGiá 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
func main() {
str := "hello world!"
reflectValue := reflect.ValueOf(str)
fmt.Println(reflectValue)
}Kết quả xuất là
hello world!Type
func (v Value) Type() TypePhương pháp Type có thể lấy được loại của một giá trị reflection
func main() {
num := 114514
rValue := reflect.ValueOf(num)
fmt.Println(rValue.Type())
}Xuất
intElem
func (v Value) Elem() ValueLấy giá trị reflection của phần tử của một giá trị reflection
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
114514Con trỏ
Có hai cách để lấy con trỏ của một giá trị reflection
// 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.PointerVí dụ như sau
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 0xc0000a6058TIP
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
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 0xc00010e4b0Thiết lập giá trị
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ử.
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
11Lấy giá trị
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
func main() {
var str string
str = "hello"
rValue := reflect.ValueOf(str)
if v, ok := rValue.Interface().(string); ok {
fmt.Println(v)
}
}Xuất
helloHà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
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
intGọi
Thông qua giá trị reflection để gọi hàm
func (v Value) Call(in []Value) []Valuefunc 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
50Struct
Giả sử có struct như sau
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
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.
type Type interface{
Field(i int) StructField
}Ví dụ truy cập thông qua chỉ mục như sau
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 falsetype Type interface{
FieldByName(name string) (StructField, bool)
}Ví dụ truy cập thông qua tên như sau
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 falseSử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
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
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ó
// 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) stringVí dụ như sau
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
nameTruy 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
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
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 trueNế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
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
stringGọ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
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
// Trả về giá trị reflection con trỏ trỏ đến giá trị reflection
func New(typ Type) ValueLấy string làm ví dụ
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
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
func MakeSlice(typ Type, len, cap int) Valuefunc 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
func MakeMapWithSize(typ Type, n int) Valuefunc 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
func MakeChan(typ Type, buffer int) Valuefunc 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
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Valuefunc 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)
1024Hoà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.
func DeepEqual(x, y any) boolHà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à
nilthì 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à
nilthì là hoàn toàn bằng nhau đều không phảinilthì 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à
nilmớ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
func main() {
a := make([]int, 100)
b := make([]int, 100)
fmt.Println(reflect.DeepEqual(a, b))
}Xuất
trueStruct
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