[go 反射] 结构体-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

[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号位为起点开始遍历传入参数
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go