[go 反射] 结构体

[go 反射] 结构体

本文你将了解

  1. 不同类型结构体直接通过反射拷贝
  2. 通过反射调用结构体方法
  3. 常踩的坑点

不同类型结构体直接通过反射拷贝

普通的拷贝

type One struct{
    Name string
    Age int
}
var one One=One{"jim",20}
var one_copy One=one//拷贝完成

反射拷贝

type One struct{
    Name string
    Age int
}
type Two struct{
    One string
    Two int
}
type Three struct{
    One string
    Two string
    Three int
    Four bool
}
type str_error string//自制简易错误类型
func (s str_error)Error()string{
    return str_error(s)
}
var one One=One{"jim",20}
var one_copy Two
var one_copyt Three
func main(){
    Struct_Copy_Strict(&one_copy,one)//下文实现的严格拷贝
    Struct_Copy(&one_copy,one)//下文实现带兼容性的拷贝
}
func Struct_Copy[T any](dst *T, src any) (err error) {
    //这里示范为了将代码精简到最简,把很多判断条件都省略了。正常使用,指针判断,类型判断,空值判断都是必不可少的!!!
	dtp, stp := reflect.TypeOf(dst).Elem(), reflect.TypeOf(src)
	dvl, svl := reflect.ValueOf(dst).Elem(), reflect.ValueOf(src)
	var i, j = 0, 0
	var dftp, sftp reflect.Type
	var dfvl reflect.Value
	var setpos []int = make([]int, 0, stp.NumField())
	for i < dtp.NumField() && j < stp.NumField() {//预确定是否能进行拷贝,确保拷贝的完整性,不会拷贝到一半不能拷贝退出,造成dst污染
		dftp, sftp = dtp.Field(i).Type, stp.Field(j).Type
		dfvl = dvl.Field(i)
		if dftp == sftp && dfvl.CanSet() {
			setpos = append(setpos, i)
		}
		i++
	}
	if len(setpos) < stp.NumField() {
		err = str_error("copy struct failed")
		return
	}
	for i = 0; i < len(setpos); i++ {
		dvl.Field(setpos[i]).Set(svl.Field(i))
	}
	return err
}
func Struct_Copy_Strict[T any](dst *T, src any) (err error) {
	dtp, stp := reflect.TypeOf(dst).Elem(), reflect.TypeOf(src)
	dvl, svl := reflect.ValueOf(dst).Elem(), reflect.ValueOf(src)
	if dtp.NumField() == stp.NumField() {
		if dtp.NumField() > 0 {
			for i := 0; i < dtp.NumField(); i++ {
				if dtp.Field(i).Type != stp.Field(i).Type && dvl.Field(i).CanSet() {
					err = str_error("can't copy two struct, because them are different")
					return
				}
			}
			for i := 0; i < dtp.NumField(); i++ {
				dvl.Field(i).Set(svl.Field(i))
			}
		}
	} else {
		err = str_error("can't copy two struct, because them are different")
	}
	return
}

反射调用结构体方法

结构体方法调用反射主要为反射调用函数,如有不清楚请查看 [go 反射] 函数

关键方法

  • NumMethod()reflect.Type接口和reflect.Value都有,查看结构体下方法数量
  • Method(int).Func得到一个reflect.Value类型,使用和正常反射使用函数一样。有一点需注意,参数会多一个指针参数,但是此参数传参时请跳过
import (
    "reflect"
    "fmt"
)
type One struct{
}
func (s One)Greet()string{
    return "hello golang"
}
func (s *One)Greet2()string{
    return "hello golang too"
}

func main(){
    var one One
    tp:=reflect.TypeOf(one)//能识别并调用Greet
    tpp:=reflect.TypeOf(&one)//能识别并调用Greet2
    fmt.Println(tp.NumMethod(),tpp.NumMethod())//1,1
    ans:=tp.Method(0).Func.Call([]reflect.Value{})
    fmt.Println(ans[0].String())
    ans=tpp.Method(0).Func.Call([]refelct.Value{})
}

常踩的坑

  1. 方法首字母大小写问题,首字母小写反射会搜不到方法
  2. 方法传参,自行通过NumIn时得到的结果会比实际需要的参数多一个,所以需要从1号位为起点开始遍历传入参数

你可能感兴趣的:(go反射,golang,开发语言,后端)