目录
golang 的反射里面,一个实例有两部分:reflect.Type 和 reflect.Value 。reflect.Type 是不可以修改的,reflect.Value可以修改的。reflect.Type 可以通过 reflect.Value 得到,但是反过来则不行。我们先看几个例子,分别讲解一下。具体的可以执行看下
一、反射读字段
func IterateFields(entity any) (map[string]any, error) {
if entity == nil {
return nil, errors.New("entiry 不能为nil")
}
ty := reflect.TypeOf(entity)
tv := reflect.ValueOf(entity)
if tv.IsZero() {
return nil, errors.New("不支持类型")
}
for tv.Kind() != reflect.Struct {
ty = ty.Elem()
tv = tv.Elem()
}
if ty.Kind() != reflect.Struct {
return nil, errors.New("不支持类型")
}
nums := ty.NumField()
result := make(map[string]any, nums)
for i := 0; i < nums; i++ {
fileType := ty.Field(i)
fileValue := tv.Field(i)
if fileType.IsExported() {
result[fileType.Name] = fileValue.Interface()
} else {
result[fileType.Name] = reflect.Zero(fileType.Type).Interface()
}
}
return result, nil
}
func TestIterateFields(t *testing.T) {
type user struct {
Name string
age int
}
testCases := []struct {
name string
entity any
wantError error
wantRes map[string]any
}{
{
name: "struct",
entity: user{Name: "liuxingyu"},
wantError: nil,
wantRes: map[string]any{"Name": "liuxingyu", "age": 0},
},
{
name: "lowercase",
entity: user{Name: "liuxingyu", age: 15},
wantError: nil,
wantRes: map[string]any{"Name": "liuxingyu", "age": 0},
},
{
name: "pointer",
entity: &user{Name: "liuxingyu"},
wantError: nil,
wantRes: map[string]any{"Name": "liuxingyu", "age": 0},
},
{
name: "multi-pointer",
entity: func() **user {
t := &user{
Name: "liuxingyu",
}
return &t
}(),
wantError: nil,
wantRes: map[string]any{"Name": "liuxingyu", "age": 0},
},
{
name: "value nil",
entity: nil,
wantError: errors.New("entiry 不能为nil"),
wantRes: nil,
},
{
name: "type nil",
entity: (*user)(nil),
wantError: errors.New("不支持类型"),
wantRes: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fields, err := IterateFields(tc.entity)
if err != nil {
assert.Equal(t, tc.wantError, err)
return
}
assert.Equal(t, tc.wantRes, fields)
})
}
}
下面我们解析几个重要的函数
// NumField returns a struct type's field count. // It panics if the type's Kind is not Struct. NumField() int
-
NumField 必须是 struct ,如果是指针就不可以,对于指针,我们可以通过 Elem() 获取指针指向的值。
-
*struct{} 就是 struct{}. **struct{} 就是 *struct{}
-
reflect.Type 以及 reflect.Value 都不能是nil
-
reflect.Type 的 Field 可以获取类型
-
reflect.Value 的 Field 可以获取值,类型是value,如果获取any 那么就要通过interface{}
-
kind: 是一个枚举值,例如是否是指针,是否是数组,是否是切片
-
reflect.Zero 可以获取类型的零值
二、用反射设置值
可以用反射来修改一个字段的值,这个很奇妙,修改的值或者是struct 必须是指针,这是一定要记住的
func SetFields(entity any, file string, val string) error {
typeVal := reflect.ValueOf(entity)
for typeVal.Type().Kind() == reflect.Pointer {
typeVal = typeVal.Elem()
}
fileval := typeVal.FieldByName(file)
if !fileval.CanSet() {
return errors.New("不可修改字段")
}
fileval.Set(reflect.ValueOf(val))
return nil
}
-
CanSet 判断是否可以修改
-
虽然说必须是指针,但是要修改值的时候必须用 Elem() 获取指针所指向值
-
FieldByName返回具有给定名称的 struct 字段
-
Set 修改值
三、输出方法信息并且执行调用
这是要通过反射执的方法放在其它包里
package types
type User struct {
Name string
age int
}
func (u User) GetAge() int {
return u.age
}
func (u *User) ChangeName(newName string) {
u.Name = newName
}
func (u User) private() {
}
func NewUser(name string, age int) User {
return User{
Name: name,
age: age,
}
}
func NewUserPtr(name string, age int) *User {
return &User{
Name: name,
age: age,
}
}
实现反射
func IterateFunc(entity any) (map[string]FuncInfo, error) {
typ := reflect.TypeOf(entity)
numMethod := typ.NumMethod()
result := make(map[string]FuncInfo, numMethod)
for i := 0; i < numMethod; i++ {
method := typ.Method(i)
fn := method.Func
numIn := fn.Type().NumIn()
input := make([]reflect.Type, 0, numIn)
intputValue := make([]reflect.Value, 0, numIn)
input = append(input, reflect.TypeOf(entity))
intputValue = append(intputValue, reflect.ValueOf(entity))
for i := 1; i < numIn; i++ {
fnIntype := fn.Type().In(i)
input = append(input, fnIntype)
intputValue = append(intputValue, reflect.Zero(fnIntype))
}
numout := fn.Type().NumOut()
outPut := make([]reflect.Type, 0, numout)
for j := 0; j < numout; j++ {
outPut = append(outPut, fn.Type().Out(j))
}
fcall := fn.Call(intputValue)
res := []any{}
for _, v := range fcall {
res = append(res, v.Interface())
}
result[method.Name] = FuncInfo{
Name: method.Name,
InputTypes: input,
OutputTypes: outPut,
Result: res,
}
}
return result, nil
}
type FuncInfo struct {
Name string
InputTypes []reflect.Type
OutputTypes []reflect.Type
Result []any
}
testing 方法
func TestIterateFunc(t *testing.T) {
testCases := []struct {
name string
entity any
wantInfo map[string]FuncInfo
wantError error
}{
{
name: "struct",
entity: types.NewUser("liuxingyu", 20),
wantInfo: map[string]FuncInfo{
"GetAge": {
Name: "GetAge",
InputTypes: []reflect.Type{reflect.TypeOf(types.User{})},
OutputTypes: []reflect.Type{reflect.TypeOf(0)},
Result: []any{20},
},
// 不可调用的方法
//"ChangeName": {
// Name: "ChangeName",
// InputTypes: []reflect.Type{reflect.TypeOf(types.User{}), reflect.TypeOf("")},
// OutputTypes: []reflect.Type{},
// Result: []any{},
//},
},
},
{
name: "pointer",
entity: types.NewUserPtr("liuxingyu", 20),
wantInfo: map[string]FuncInfo{
"GetAge": {
Name: "GetAge",
InputTypes: []reflect.Type{reflect.TypeOf(&types.User{})},
OutputTypes: []reflect.Type{reflect.TypeOf(0)},
Result: []any{20},
},
"ChangeName": {
Name: "ChangeName",
InputTypes: []reflect.Type{reflect.TypeOf(&types.User{}), reflect.TypeOf("")},
OutputTypes: []reflect.Type{},
Result: []any{},
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
iterateFunc, err := IterateFunc(tc.entity)
if err != nil {
assert.Equal(t, tc.wantError, err)
return
}
assert.Equal(t, tc.wantInfo, iterateFunc)
})
}
}
-
通过测试方法大家可以以下结论,注释的是不可以调用的方法
-
包的私有方法反射不可以调用
-
调用者是指针对于反射来说指针和非指针的非私有方法可以调用
-
调用者是非指针对于反射来说只能调用非指针的非私有方法可以调用
-
NumMethod() 获取方法的个数,通过循环分别执行
-
Method 获取 每个方法的信息包括方法名以及方法的函数
-
其他的函数都很好理解,看见函数名就知道什么意思,有一个我需要讲一下
input = append(input, reflect.TypeOf(entity))
intputValue = append(intputValue, reflect.ValueOf(entity))
输入的第一个参数是 reflect.TypeOf(entity),代表的是结构体也可能是结构体指针。
比如 这个方法
func (u User) GetAge() int { return u.age }
它的变形就是 func GetAge(u User) int { return u.age }
方法其实就是函数的一种变形,可以理解成golang 的语法糖,所以在反射时,对于输入参数,就默认会有一个参数所以需要这么写
四、反射遍历(数组,切片,map)
func iterateSlice(ite any) ([]any, error) {
tvl := reflect.ValueOf(ite)
var result []any
for i := 0; i < tvl.Len(); i++ {
result = append(result, tvl.Index(i).Interface())
}
return result, nil
}
func iterateMap(ite map[any]any) ([]any, []any, error) {
tvl := reflect.ValueOf(ite)
var keys []any
var valus []any
itr := tvl.MapRange()
for itr.Next() {
keys = append(keys, itr.Key().Interface())
valus = append(valus, itr.Value().Interface())
}
return keys, valus, nil
}
func Test_iterateSlice(t *testing.T) {
testCases := []struct {
name string
entry any
wantVal []any
wantError error
}{
{
name: "array",
entry: [3]int{1, 2, 3},
wantVal: []any{1, 2, 3},
},
{
name: "slice",
entry: []int{1, 2, 3},
wantVal: []any{1, 2, 3},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
slice, err := iterateSlice(tc.entry)
assert.Equal(t, tc.wantError, err)
if err != nil {
return
}
assert.Equal(t, tc.wantVal, slice)
})
}
}
func Test_iterateMap(t *testing.T) {
testCases := []struct {
name string
entry map[any]any
wantkey []any
wantVal []any
wantError error
}{
{
name: "array",
entry: map[any]any{"a": 1, "b": 2},
wantVal: []any{1, 2},
wantkey: []any{"a", "b"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
keys, values, err := iterateMap(tc.entry)
assert.Equal(t, tc.wantError, err)
if err != nil {
return
}
assert.Equal(t, tc.wantVal, values)
assert.Equal(t, tc.wantkey, keys)
})
}
}
比较简单,就不用多说了,可以运行