目录根据类型做不同处理标准库json中的示例基本类型的反射数组类型的反射chan反射map反射迭代反射map对象slice反射string反射interface/Pointer反射结...
目录
根据类型做不同处理标准库 json 中的示例
基本类型的反射
数组类型的反射
chan 反射
map 反射
迭代反射 map 对象
slice 反射
string 反射
interface/Pointer 反射
结构体的反射
遍历结构体字段
根据名称或索引获取结构体字段
修改结构体字段
结构体方法调用
是否实现接口
结构体的 tag
修改结构体未导字段
方法的反射
入参和返回值
通过反射调用方法
总结
在之前的两篇文章 《深入理解 go reflect - 反射基本原理》、《深入理解 go reflect - 要不要传指针》 中, 我们讲解了关于 go 反射的一些基本原理,以及通过反射对象修改变量的一些注意事项。 本篇文章将介绍一些常见的反射用法,涵盖了常见的数据类型的反射操作。
根据类型做不同处理
Kind
func getType(i interface{}) string { v := reflect.ValueOf(i) switch v.Kind() { case reflect.Bool: b := "false" if v.Bool() { b = "true" } return fmt.Sprintf("bool: %s", b) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return fmt.Sprintf("int: %d", v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return fmt.Sprintf("uint: %d", v.Uint()) case reflect.Float32, reflect.Float64: return fmt.Sprintf("float: %.1f", v.Float()) case reflect.String: return fmt.Sprintf("string: %s", v.String()) case reflect.Interface: return fmt.Sprintf("interface: %v", v.Interface()) case reflect.Struct: return fmt.Sprintf("struct: %v", v.Interface()) case reflect.Map: return fmt.Sprintf("map: %v", v.Interface()) case reflect.Slice: return fmt.Sprintf("slice: %v", v.Interface()) case reflect.Array: return fmt.Sprintf("array: %v", v.Interface()) case reflect.Pointer: return fmt.Sprintf("pointer: %v", v.Interface()) case reflect.Chan: return fmt.Sprintf("chan: %v", v.Interface()) default: return "unknown" } } func TestKind(t *testing.T) { assert.Equal(t, "int: 1", getType(1)) assert.Equal(t, "string: 1", getType("1")) assert.Equal(t, "bool: true", getType(true)) assert.Equal(t, "float: 1.0", getType(1.0)) arr := [3]int{1, 2, 3} sli := []int{1, 2, 3} assert.Equal(t, "array: [1 2 3]", getType(arr)) assert.Equal(t, "slice: [1 2 3]", getType(sli)) }
标准库 json 中的示例
encoding/jsonKind
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { // ... 其他代码 switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder // ...省略其他 case... default: return unsupportedTypeEncoder } }
jsonencoder
基本类型的反射
int*uint*float*complex*bool
panic
func TestBaseKind(t *testing.T) { // 通过反射修改 int 类型变量的值 a := 1 v := reflect.ValueOf(&a) v.Elem().SetInt(10) assert.Equal(t, 10, a) // 通过反射修改 uint16 类型变量的值 b := uint16(10) v1 := reflect.ValueOf(&b) v1.Elem().SetUint(20) assert.Equal(t, uint16(20), b) // 通过反射修改 float32 类型变量的值 f := float32(10.0) v2 := reflect.ValueOf(&f) v2.Elem().SetFloat(20.0) assert.Equal(t, float32(20.0), f) }
Elem()
数组类型的反射
IndexSet
func TestArray(t *testing.T) { // 通过反射修改数组元素的值 arr := [3]int{1, 2, 3} v := reflect.ValueOf(&arr) // 修改数组中的第一个元素 v.Elem().Index(0).SetInt(10) assert.Equal(t, [3]int{10, 2, 3}, arr) }
chan 反射
chanchan
func TestChan(t *testing.T) { // 通过反射修改 chan ch := make(chan int, 1) v := reflect.ValueOf(&ch) // 通过反射对象向 chan 发送数据 v.Elem().Send(reflect.ValueOf(2)) // 在反射对象外部从 chan 接收数据 assert.Equal(t, 2, <-ch) }
map 反射
mapSetMapIndexmapkey
func TestMap(t *testing.T) { // 通过反射修改 map 元素的值 m := map[string]int{"a": 1} v := reflect.ValueOf(&m) // 修改 a 的 key,修改其值为 2 v.Elem().SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(2)) // 外部的 m 可以看到反射对象的修改 assert.Equal(t, 2, m["a"]) }
迭代反射 map 对象
MapRangemap
func TestIterateMap(t *testing.T) { // 遍历 map m := map[string]int{"a": 1, "b": 2} v := reflect.ValueOf(m) // 创建 map 迭代器 iter := v.MapRange() // 迭代 map 的元素 for iter.Next() { // a 1 // b 2 fmt.Println(iter.Key(), iter.Value()) } }
slice 反射
sliceIndexSet*
func TestSlice(t *testing.T) { // 通过反射修改 slice 元素的值 sli := []int{1, 2, 3} v := reflect.ValueOf(&sli) v.Elem().Index(0).SetInt(10) assert.Equal(t, []int{10, 2, 3}, sli) }
string 反射
stringString
func TestString(t *testing.T) { // 通过反射修改字符串的值 s := "hello" v := reflect.ValueOf(&s) v.Elem().SetString("world") assert.Equal(t, "world", s) }
interface/Pointer 反射
interfacePointerElem
func TestPointer(t *testing.T) { a := 1 // 接口类型 var i interface{} = &a v1 := reflect.ValueOf(i) v1.Elem().SetInt(10) assert.Equal(t, 10, a) // 指针类型 var p = &a v2 := reflect.ValueOf(p) v2.Elem().SetInt(20) assert.Equal(t, 20, a) }
Elem
结构体的反射
对于 go 中的结构体,反射系统中为我们提供了很多操作结构体的方法,比如获取结构体的字段、方法、标签、通过反射对象调用其方法等。
先假设我们有如下结构体:
type Person struct { Name string Age int sex uint8 } func (p Person) M1() string { return "person m1" } func (p *Person) M2() string { return "person m2" }
遍历结构体字段
NumFieldField
func TestStruct1(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(p) // string Tom // int 18 // uint8 1 for i := 0; i < v.NumField(); i++ { fmt.Println(v.Field(i).Type(), v.Field(i)) } }
根据名称或索引获取结构体字段
我们可以根据结构体字段的名称或索引来获取结构体的字段:
func TestStruct2(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(p) assert.Equal(t, 18, v.Field(1).Interface()) assert.Equal(t, 18, v.FieldByName("Age").Interface()) assert.Equal(t, 18, v.FieldByIndex([]int{1}).Interface()) }
修改结构体字段
FieldSet*
func TestStruct2(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(&p) v.Elem().FieldByName("Name").SetString("Jack") assert.Equal(t, "Jack", p.Name) }
NamestringSetStringintSetInt
结构体方法调用
通过反射对象来调用结构体的方法时,需要注意的是,如果我们需要调用指针接收者的方法,则需要传递地址:
func TestStruct3(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} // 值接收者(receiver) v1 := reflect.ValueOf(p) assert.Equal(t, 1, v1.NumMethod()) // 注意:值接收者没有 M2 方法 assert.False(t, v1.MethodByName("M2").IsValid()) // 通过值接收者调用 M1 方法 results := v1.MethodByName("M1").Call(nil) assert.Len(t, results, 1) assert.Equal(t, "person m1", results[0].Interface()) // 指针接收者(pointer receiver) v2 := reflect.ValueOf(&p) assert.Equal(t, 2, v2.NumMethod()) // 通过指针接收者调用 M1 和 M2 方法 results = v2.MethodByName("M1").Call(nil) assert.Len(t, results, 1) assert.Equal(t, "person m1", results[0].Interface()) results = v2.MethodByName("M2").Call(nil) assert.Len(t, results, 1) assert.Equal(t, "person m2", results[0].Interface()) }
说明:
reflect.ValueOf
reflect.ValueOf
MethodByNameIsValidfalse
Callnil
Callreflect.Valuereflect.Value
是否实现接口
对于这个,其实有一个更简单的方法,那就是利用接口断言:
func TestStrunct4_0(t *testing.T) { type TestInterface interface { M1() string } var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(p) // v.Interface() 返回的是 interface{} 类型 // v.Interface().(TestInterface) 将 interface{} 类型转换为 TestInterface 类型 v1, ok := v.Interface().(TestInterface) assert.True(t, ok) assert.Equal(t, "person m1", v1.M1()) }
TypeImplements
func TestStruct4(t *testing.T) { type TestInterface interface { M1() string } var p = Person{Name: "Tom", Age: 18, sex: 1} typ := reflect.TypeOf(p) typ1 := reflect.TypeOf((*TestInterface)(nil)).Elem() assert.True(t, typ.Implements(typ1)) }
结构体的 tag
validatorTypetag
type Person1 struct { Name string `json:"name"` } func TestStruct5(t *testing.T) { var p = Person1{Name: "Tom"} typ := reflect.TypeOf(p) tag := typ.Field(0).Tag assert.Equal(t, "name", tag.Get("json")) }
修改结构体未导字段
我们知道,结构体的字段如果首字母小写,则是未导出的,不能被外部包访问。但是我们可以通过反射修改它:
func TestStruct6(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(&p) // 下面这样写会报错: // panic: reflect: reflect.Value.SetInt using value obtained using unexported field // v.Elem().FieldByName("sex").SetInt(0) ft := v.Elem().FieldByName("sex") sexV := reflect.NewAt(ft.Type(), unsafe.Pointer(ft.UnsafeAddr())).Elem() assert.Equal(t, 1, p.sex) // 修改前是 1 sexV.Set(reflect.ValueOf(uint8(0))) // 将 sex 字段修改为 0 assert.Equal(t, 0, p.sex) // 修改后是 0 }
NewAtsexsex
方法的反射
这里说的方法包括函数和结构体的方法。
入参和返回值
reflectInOut
func (p Person) Test(a int, b string) int { return a } func TestMethod(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(p) m := v.MethodByName("Test") // 参数个数为 2 assert.Equal(t, 2, m.Type().NumIn()) // 返回值个数为 1 assert.Equal(t, 1, m.Type().NumOut()) // In(0) 是第一个参数,In(1) 是第二个参数 arg1 := m.Type().In(0) assert.Equal(t, "int", arg1.Name()) arg2 := m.Type().In(1) assert.Equal(t, "string", arg2.Name()) // Out(0) 是第一个返回值 ret0 := m.Type().Out(0) assert.Equal(t, "int", ret0.Name()) }
说明:
InOutreflect.TypeName
NumInNumOut
reflect.ValueMethodByName
通过反射调用方法
reflect.ValueCall
func TestMethod2(t *testing.T) { var p = Person{Name: "Tom", Age: 18, sex: 1} v := reflect.ValueOf(p) // 通过反射调用 Test 方法 m := v.MethodByName("Test") arg1 := reflect.ValueOf(1) arg2 := reflect.ValueOf("hello") args := []reflect.Value{arg1, arg2} rets := m.Call(args) assert.Len(t, rets, 1) assert.Equal(t, 1, rets[0].Interface()) }
说明:
Call[]reflect.Valuereflect.Value
Call[]reflect.Value
reflect.ValueMethodByName
Call
总结
reflect.KindKindKind
reflect.Value
chanchan
SetMapIndexmapMapRangemap
IndexsliceSetIndexslice
SetStringstring
interfacePointerElemElemSet*
reflecttag
NewAt
Call