- 2021/02/09, Golang 反射简单示例
- 2021/02/17, Golang
reflect
包 - 2021/04/06, Go 反射
通过反射(Reflection),我们可以在程序运行时,动态的增删该一些信息(类型,方法,变量),用来完成一些特殊的任务。
主要是实现不同类型都支持的一些方法。在 ORM,序列化等方面都有应用。
这种动态获取信息的能力让程序的开发变得更加灵活,但也会损失部分性能,毕竟需要进行很多类型检查、值转换等操作。
而且,滥用的话会导致程序变得复杂,可读性下降。
示例 1
type Person struct {
Name string
Age int
}
func Insert(db *sql.DB, v interface{}) error {
value := reflect.ValueOf(v)
typ := reflect.TypeOf(v)
sql := "INSERT INTO persons ("
placeholders := "VALUES ("
for i := 0; i < typ.Elem().NumField(); i++ {
fieldName := typ.Elem().Field(i).Name
sql += fmt.Sprintf("%s, ", fieldName)
placeholders += "?, "
}
sql = sql[:len(sql)-2] + ")"
placeholders = placeholders[:len(placeholders)-2] + ")"
args := make([]interface{}, typ.Elem().NumField())
for i := 0; i < typ.Elem().NumField(); i++ {
args[i] = value.Elem().Field(i).Interface()
}
_, err := db.Exec(sql+placeholders, args...)
return err
}
示例 2
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
Address string
}
// 定义结构体方法
func (p Person) Hello() string {
return "Hello, " + p.Name
}
func main() {
p := Person{
Name: "Alice",
Age: 30,
Address: "123 Main St",
}
fmt.Println("获取变量的反射类型:===============")
pType := reflect.TypeOf(p) // &reflect.rtype
fmt.Printf("Type: %s, %#v\n", pType, pType) // main.Person
// fmt.Println("Type:", pType.Elem()) // panic: reflect: Elem of invalid type main.Person
pType2 := reflect.TypeOf(&p)
fmt.Printf("Type: %s, %#v\n", pType2, pType2) // *main.Person
fmt.Printf("Type: %s, %#v\n", pType2.Elem(), pType2.Elem()) // main.Person (指针类型的基础类型)
fmt.Println("获取变量的反射值:===============")
pValue := reflect.ValueOf(p) // main.Person
fmt.Printf("Value: %s, %#v\n", pValue, pValue)
pValue2 := reflect.ValueOf(&p)
fmt.Printf("Value: %s, %#v\n", pValue2, pValue2) // 地址
fmt.Printf("Value: %s, %#v\n", pValue2.Elem(), pValue2.Elem())
fmt.Println("遍历结构体字段:===============")
for i := 0; i < pType.NumField(); i++ {
field := pType.Field(i)
value := pValue.Field(i)
fmt.Printf("%s: %v\n", field.Name, value.Interface())
}
fmt.Println("修改结构体字段值:===============")
ageField1 := pValue.FieldByName("Age")
fmt.Printf("Value: %#v\n", ageField1)
// ageField1.SetInt(31) // panic: reflect: reflect.Value.SetInt using unaddressable value
// ageField1.Elem().SetInt(31) // panic: reflect: call of reflect.Value.Elem on int Value
ageField2 := pValue2.Elem().FieldByName("Age")
fmt.Printf("Value: %#v\n", ageField2)
ageField2.SetInt(31) // 只有指针类型才能通过反射修改
fmt.Println("New Age:", p.Age)
fmt.Println("调用结构体方法:===============")
helloMethod := pValue.MethodByName("Hello")
result := helloMethod.Call(nil)
fmt.Println(result)
// panic: reflect: call of reflect.Value.FieldByName on ptr Value
pValue2.FieldByName("Age")
// panic: reflect: reflect.Value.SetInt using unaddressable value
// a := reflect.Indirect(pValue)
b := reflect.Indirect(pValue2)
b.FieldByName("Age").SetInt(33)
fmt.Printf("%#v\n", b)
}
reflect.TypeOf
:获取变量的类型reflect.ValueOf
:获取变量的值Type.NumField
:获取结构体字段数量-
Type.Field
:获取结构体字段信息 -
Value.Field
:获取结构体字段的值 Value.FieldByName
:根据字段名获取结构体字段的值Value.SetInt
:设置整数类型字段的值Value.MethodByName
:根据方法名获取结构体方法Value.Call
:调用结构体方法