Skip to content
鼓励作者:欢迎打赏犒劳

go反射

获取对象信息

简单场景

go
package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	p := Person{Name: "Alice", Age: 30}
	t := reflect.TypeOf(p)

	fmt.Println("Type:", t.Name()) // Person
	fmt.Println("Kind:", t.Kind()) // struct

	// 遍历字段
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Printf("Field: %s, Type: %s, Tag: %s\n",
			field.Name, field.Type, field.Tag.Get("json"))
	}
}
text
Type: Person
Kind: struct
Field: Name, Type: string, Tag: name
Field: Age, Type: int, Tag: age

比较复杂的tag

go
package main

import (
	"fmt"
	"reflect"
	"strings"
)

type User struct {
	Name  string `json:"name" validate:"required,min=2" gorm:"size:255"`
	Age   int    `json:"age" validate:"gte=0,lte=150"`
	Email string `json:"email" validate:"required,email"`
}

func main() {
	var u User
	t := reflect.TypeOf(u)

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Printf("字段: %s\n", field.Name)

		// 获取 validate tag
		validateTag := field.Tag.Get("validate")
		if validateTag == "" {
			fmt.Println("  无 validate 标签")
			continue
		}

		fmt.Printf("  原始 validate tag: %s\n", validateTag)

		// 解析 validate tag 中的多个规则
		rules := parseValidateTag(validateTag)
		for k, v := range rules {
			if v == "" {
				fmt.Printf("  规则: %s\n", k)
			} else {
				fmt.Printf("  规则: %s=%s\n", k, v)
			}
		}
		fmt.Println()
	}
}

// 解析 validate tag,如 "required,min=2" -> map[required:true min:2]
func parseValidateTag(tag string) map[string]string {
	rules := make(map[string]string)
	parts := strings.Split(tag, ",")

	for _, part := range parts {
		kv := strings.Split(part, "=")
		key := kv[0]
		if len(kv) == 1 {
			rules[key] = "" // 如 required
		} else {
			rules[key] = kv[1] // 如 min=2
		}
	}

	return rules
}
text
字段: Name
  原始 validate tag: required,min=2
  规则: min=2
  规则: required

字段: Age
  原始 validate tag: gte=0,lte=150
  规则: gte=0
  规则: lte=150

字段: Email
  原始 validate tag: required,email
  规则: required
  规则: email

封装通用的工具解析tag

go
// main.go
package main

import (
	"fmt"
	"reflect"
	"strings"
)

// 示例结构体,包含复杂的 validate tag
type User struct {
	Name    string `json:"name" validate:"required,min=2,max=50"`
	Email   string `json:"email" validate:"required,email"`
	Age     int    `json:"age" validate:"gte=0,lte=150"`
	Active  bool   `json:"active" validate:"omitempty,boolean"`
	Roles   []string `json:"roles" validate:"dive,oneof=admin user guest"` // dive 表示验证切片内部元素
	Profile string `json:"profile" validate:"-"` // - 表示跳过
}

func main() {
	var u User
	t := reflect.TypeOf(u)

	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		fmt.Printf("字段名: %s\n", field.Name)

		// 获取 validate tag 的原始字符串
		tagValue := field.Tag.Get("validate")
		if tagValue == "" || tagValue == "-" {
			fmt.Println("  ⚠️  无 validate 规则")
			fmt.Println()
			continue
		}

		fmt.Printf("  原始 tag: %s\n", tagValue)

		// 解析成规则列表
		rules := ParseTagRules(tagValue)

		// 打印每条规则
		for key, value := range rules {
			if value == "" {
				fmt.Printf("    🔹 %s (无参数)\n", key)
			} else {
				fmt.Printf("    🔹 %s = %q\n", key, value)
			}
		}
		fmt.Println()
	}
}

// ParseTagRules 将如 "required,min=6,gte=0" 的 tag 解析为 map[key]value
// 支持: required (flag), min=6 (key-value)
func ParseTagRules(tag string) map[string]string {
	rules := make(map[string]string)

	// 按逗号分割
	parts := strings.Split(tag, ",")
	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part == "" || part == "-" {
			continue
		}

		// 判断是否有 =
		if equalIndex := strings.Index(part, "="); equalIndex > 0 {
			key := part[:equalIndex]
			value := part[equalIndex+1:]
			rules[key] = value
		} else {
			// 没有 =,就是 flag 类型,值设为空字符串表示存在
			rules[part] = ""
		}
	}

	return rules
}
text
字段名: Name
  原始 tag: required,min=2,max=50
    🔹 min = "2"
    🔹 max = "50"
    🔹 required (无参数)

字段名: Email
  原始 tag: required,email
    🔹 required (无参数)
    🔹 email (无参数)

字段名: Age
  原始 tag: gte=0,lte=150
    🔹 gte = "0"
    🔹 lte = "150"

字段名: Active
  原始 tag: omitempty,boolean
    🔹 omitempty (无参数)
    🔹 boolean (无参数)

字段名: Roles
  原始 tag: dive,oneof=admin user guest
    🔹 dive (无参数)
    🔹 oneof = "admin user guest"

字段名: Profile
  ⚠️  无 validate 规则

使用框架实现复杂tag校验

go
// main.go
package main

import (
	"fmt"
	"log"

	"github.com/go-playground/validator/v10"
)

// 初始化 validator
var validate *validator.Validate

func init() {
	validate = validator.New()
}

// 登录请求结构体
type LoginReq struct {
	Email    string `json:"email" validate:"required,email"`
	Password string `json:"password" validate:"required,min=6"`
	Age      int    `json:"age" validate:"gte=0,lte=150"`           // 年龄 0-150
	Role     string `json:"role" validate:"oneof=admin user guest"` // 角色必须是其中之一
}

func main() {
	// 场景1:测试一个合法的请求
	fmt.Println("✅ 测试合法请求:")
	validReq := LoginReq{
		Email:    "alice@example.com",
		Password: "123456",
		Age:      25,
		Role:     "user",
	}
	err := validate.Struct(validReq)
	if err != nil {
		log.Println("校验失败:", err)
	} else {
		fmt.Println("通过校验:", validReq)
	}

	// 场景2:测试非法请求(邮箱格式错误 + 密码太短)
	fmt.Println("\n❌ 测试非法请求:")
	invalidReq := LoginReq{
		Email:    "not-an-email", // ❌ 邮箱格式错误
		Password: "123",          // ❌ 密码太短
		Age:      200,            // ❌ 年龄超标
		Role:     "hacker",       // ❌ 角色非法
	}
	err = validate.Struct(invalidReq)
	if err != nil {
		// 类型断言:将 error 转为 validator.ValidationErrors 以获取详细信息
		if validationErrors, ok := err.(validator.ValidationErrors); ok {
			fmt.Println("详细校验错误:")
			for _, e := range validationErrors {
				fmt.Printf("字段: %s, Tag: %s, 值: %v, 条件: %s\n",
					e.Field(), e.Tag(), e.Value(), e.Param())
			}
		} else {
			fmt.Println("未知错误:", err)
		}
	} else {
		fmt.Println("意外通过校验:", invalidReq)
	}
}
text
✅ 测试合法请求:
通过校验: {alice@example.com 123456 25 user}

❌ 测试非法请求:
详细校验错误:
字段: Email, Tag: email, 值: not-an-email, 条件: 
字段: Password, Tag: min, 值: 123, 条件: 6
字段: Age, Tag: lte, 值: 200, 条件: 150
字段: Role, Tag: oneof, 值: hacker, 条件: admin user guest

获取并修改值(需传指针)

go
package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	// 调用
	p := Person{Name: "Alice", Age: 30}
	modifyValue(&p)
	fmt.Println(p) // {Bob 30}
}
func modifyValue(i interface{}) {
	v := reflect.ValueOf(i)

	// 必须是指针才能修改
	if v.Kind() != reflect.Ptr || !v.Elem().CanSet() {
		panic("need a pointer to a settable value")
	}

	e := v.Elem() // 解引用

	if e.Kind() == reflect.Struct {
		nameField := e.FieldByName("Name")
		if nameField.CanSet() {
			nameField.SetString("Bob")
		}
	}
}

利用反射执行方法

go
package main

import (
	"fmt"
	"reflect"
)

type Greeter struct{}

func (g Greeter) SayHello(name string) {
	fmt.Println("Hello,", name)
}

func callMethod(obj interface{}, methodName string, args ...interface{}) {
	v := reflect.ValueOf(obj)
	method := v.MethodByName(methodName)

	in := make([]reflect.Value, len(args))
	for i, arg := range args {
		in[i] = reflect.ValueOf(arg)
	}

	method.Call(in)
}

func main() {
	// 调用
	g := Greeter{}
	callMethod(g, "SayHello", "World") // Hello, World
}

如有转载或 CV 的请标注本站原文地址