什么是反射?

反射提供了在运行时得到变量类型和值的能力.

为什么需要反射?

既然变量都是自己定义的,为什么需要在运行时得到类型和值的信息呢?

首先了解一个神奇的概念:空接口(empty interface).一个没有定义任何方法的接口就是空接口,也就是说任何类型的变量都可以转化成空接口类型.

The empty interface is extremely useful when we are declaring a function with unknown parameters and data types.

当定义一个函数时,他的参数类型没有办法固定下来,空接口将非常有用.

例如:

package main

import (
	"fmt"
	"reflect"
)

type vocation struct {
	id    int
	title string
}

type employee struct {
	id   int
	name string
}

func createQuery(p interface{}) {
	t := reflect.TypeOf(p)
	k := t.Kind()

	fmt.Println("type is ", t)
	fmt.Println("kind is ", k)

	if k == reflect.Struct {
		value := reflect.ValueOf(p)
		numField := value.NumField()
		for i := 0; i < numField; i++ {
			fmt.Printf("field %d, type %T, value %v\n", i, value.Field(i), value.Field(i))
		}
	}
}

func main() {
	var manager = vocation{id: 5, title: "manager"}
	var xiaoHong = employee{id: 30, name: "xiaohong"}

	createQuery(manager)
	createQuery(xiaoHong)
}

使用示例:

反射修改值信息

package main

import (
	"fmt"
	"reflect"
)

//反射修改值
func reflect_set_value(a interface{}) {
	t := reflect.TypeOf(a)
	fmt.Println("type is ", t)
	v := reflect.ValueOf(a)
	k := v.Kind()
	switch k {
	case reflect.Float64:
		// 反射修改值
		v.SetFloat(6.9) // panic: reflect: reflect.Value.SetFloat using unaddressable value
		fmt.Println("a is ", v.Float())
	case reflect.Ptr:
		// Elem()获取地址指向的值
		v.Elem().SetFloat(7.9)
		fmt.Println("case:", v.Elem().Float())
		// 地址
		fmt.Println(v.Pointer())
	}
}

func main() {
	var x float64 = 3.4
	//reflect_set_value(x)
	//fmt.Println("main:", x)
	// 反射认为下面是指针类型,不是float类型
	reflect_set_value(&x)
	fmt.Println("main:", x)
}

首先,要修改值,必须传入指针类型

其次,传入指针,在反射时,得到的是reflect.Ptr而不是reflect.Float64;此时,需要用v.Elem()取到值然后再设置正确的值.

查看结构体的值,字段和方法

package main

import (
	"fmt"
	"reflect"
)

// 定义结构体
type User struct {
	Id   int
	Name string
	Age  int
}

// 绑方法
func (u User) Hello() {
	fmt.Println("Hello, I'm ", u.Name)
}

// 传入interface{}
func Poni(o interface{}) {
	t := reflect.TypeOf(o)
	fmt.Println("类型:", t)
	fmt.Println("字段:", t.Name())
	// 获取值
	v := reflect.ValueOf(o)
	fmt.Println(v)
	// 可以获取所有属性
	// 获取结构体字段个数:t.NumField()
	for i := 0; i < t.NumField(); i++ {
		// 取每个字段
		f := t.Field(i)
		fmt.Printf("%s : %v\t", f.Name, f.Type)
		// 获取字段的值信息
		// Interface():获取字段对应的值
		//val := v.Field(i).Interface()
		val := v.Field(i)
		fmt.Println("val :", val)
	}
	fmt.Println("=================方法====================")
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Println(m.Name)
		fmt.Println(m.Type)
	}

}

func main() {
	u := User{1, "xh", 20}
	Poni(u)
}

注意value从v中获取,type和method从t中获取.因为type和method都存储在iTable中.

查看匿名字段

package main

import (
	"fmt"
	"reflect"
)

// 定义结构体
type User struct {
	Id   int
	Name string
	Age  int
}

// 匿名字段
type Boy struct {
	User
	Addr string
}

func main() {
	m := Boy{User{1, "zs", 20}, "bj"}
	t := reflect.TypeOf(m)
	fmt.Println(t)
	// Anonymous:匿名
	fmt.Printf("%#v\n", t.Field(0))
	// 值信息
	fmt.Printf("%#v\n", reflect.ValueOf(m).Field(0))
}

修改结构体的值

package main

import (
	"fmt"
	"reflect"
)

// 定义结构体
type User struct {
	Id   int
	Name string
	Age  int
}

// 修改结构体值
func SetValue(o interface{}) {
	v := reflect.ValueOf(o)
	// 获取指针指向的元素
	v = v.Elem()
	// 取字段
	f := v.FieldByName("Name")
	if f.Kind() == reflect.String {
		f.SetString("xiaoming")
	}
}

func main() {
	u := User{1, "xiaohong", 20}
	SetValue(&u)
	fmt.Println(u)
}

原理跟上述修改float类型的值类似,多了一个根据name取结构体field的功能.

调用方法

package main

import (
	"fmt"
	"reflect"
)

// 定义结构体
type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Hello(name string) {
	fmt.Println("Hello ", name, " My name is ", u.Name)
}

func main() {
	u := User{1, "xiaohong", 20}
	v := reflect.ValueOf(u)
	// 获取方法
	m := v.MethodByName("Hello")
	// 构建一些参数
	args := []reflect.Value{reflect.ValueOf("xiaoming")}
	// 没参数的情况下:var args2 []reflect.Value    *****调用无参数方法时,也需传入空参数
	// 调用方法,需要传入方法的参数
	m.Call(args)
}

*获取字段的tag*

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name string `json:"tag1" db:"tag2"`
}

func main() {
	var s Student
	v := reflect.ValueOf(&s)
	// 类型
	t := v.Type()
	// 获取字段
	f := t.Elem().Field(0)
	fmt.Println(f.Tag.Get("json"))
	fmt.Println(f.Tag.Get("db"))

	v = reflect.ValueOf(s)
	t = v.Type()
	f = t.Field(0)
	fmt.Println(f.Tag.Get("json"))
	fmt.Println(f.Tag.Get("db"))
}

参考: