...menustart
- Reflect 反射
- 9.4.1 reflect.Type
...menuend
- 反射建立在 类型系统上
- 静态类型Type 和底层类型Kind
- go每个变量都有,且只有一个静态类型
- 如下,i,j 具有共同的底层类型 int, 但它们的静态类型并不一样;不经过类型转换直接相互赋值时,编译器会报错.
type MyInt int
var i int
var j MyInt
- 类型的 一个重要的分类是 接口类型(interface)
- 每个接口类型都代表固定的方法集合
- 一个非常非常重要的接口类型是空接口:
interface{}
- Interface变量存储一对值:
- 赋给该变量的具体的值、值类型的描述符。
- 值就是实现该接口的底层数据, 类型是底层数据类型的描述。
var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
r = tty
- 在这个例子中,变量 r 在结构上包含一个 (value, type) 对:
(tty, os.File)
- (value, type) 对中的 type 必须是 具体的类型(struct或基本类型),不能是 接口类型。
- 因为 接口类型不能存储接口变量。
- 从用法上来讲,反射提供了一种机制,允许程序在运行时检查接口变量内部存储的 (value, type) 对.
- 我们称 reflect.Value , reflect.Type 这两个类型为 反射类型 。它们使得访问接口内的数据成为可能。
- 它们对应两个简单的方法,分别是: reflect.ValueOf 和 reflect.TypeOf
- 分别用来读取接口变量的 reflect.Value 和 reflect.Type 部分
- 当然,从 reflect.Value 也很容易获取到 reflect.Type.
- reflect.Type和reflect.Value 不是并列关系,其实它们是一种包含关系
- 从类型角度来看,reflect.Value是一个关于<类型, 实际的值>的二元组 , 而reflect.Type是值的类型
- 从方法角度来看
- reflect.TypeOf 和 (reflect.ValueOf(x)).Type都可以返回reflect.Type
- (reflect.ValueOf(x)).Float可以返回实际的值
- (reflect.ValueOf(x)).Kind可以返回一个常量定义的类型。
- 首先,我们下看 reflect.TypeOf:
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
}
// will print
type: float64
- 为什么没看到接口?
- 调用 reflect.TypeOf(x) 时,x 被存储在一个空接口变量中被传递过去; 然后reflect.TypeOf 对空接口变量进行拆解,恢复其类型信息。
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type
- 函数 reflect.ValueOf 也会对底层的值进行恢复
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x))
// will print
value: <float64 Value>
- 类型 reflect.Type 和 reflect.Value 都有很多方法 可以使用
- reflect.Value 有一个方法 Type(),它会返回一个 reflect.Type 类型的对象
- Type和 Value都有一个名为 Kind 的方法,它会返回一个常量,表示底层数据的类型
- 常见值有:Uint、Float64、Slice等。
- Kind() 方法不会区分类似上面的 MyInt,int ,都返回 reflect.Int
- Value类型也有一些类似于Int、Float的方法,用来提取底层的数据。
- Int方法用来提取 int64, Float方法用来提取 float64
- 还有一些用来修改数据的方法,比如SetInt、SetFloat , 这个牵涉到 “可修改性”(settability)
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
// will print
type: float64
kind is float64: true
value: 3.4
- 根据一个 reflect.Value 类型的变量,我们可以使用 Interface 方法恢复其接口类型的值。
- 事实上,这个方法会把 type 和 value 信息打包并填充到一个接口变量中,然后返回。
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}
var x MyInt = 7
v := reflect.ValueOf(x)
y := v.Interface().(float64) // y will have type float64.
- 事实上,我们可以更好地利用这一特性
- 标准库中的 fmt.Println 和 fmt.Printf 等函数都接收空接口变量作为参数
- fmt 包内部会对接口变量进行拆包
fmt.Println(v.Interface())
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
- 这里问题不在于值 7.1 不能被寻址,而是因为变量 v 是“不可写的”。
- “可写性”是反射类型变量的一个属性,但不是所有的反射类型变量都拥有这个属性。
fmt.Println("settability of v:", v.CanSet())
// settability of v: false
- 这里,传递给 reflect.ValueOf 函数的是变量 x 的一个拷贝。
- 假设如果
v.SetFloat(7.1)
执行成功了,x的拷贝被顺利修改, 而实际上x 却没有改变。 - 这种操作毫无意义,而且容易产生bug.
- “可写性”就是为了避免这个问题而设计的。
- 假设如果
- 如果你想通过反射修改变量 x,就要把想要修改的变量的指针传递给 反射库。
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())
// will print
type of p: *float64
settability of p: false
- 这里,反射对象 p 也是不可写的, 但是我们也不想修改 p,事实上我们要修改的是 *p。
- 为了得到 p 指向的数据,可以调用 Value 类型的 Elem 方法。
- Elem 方法能够对指针进行“解引用”,然后将结果存储到反射 Value类型对象 v中:
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())
// settability of v: true
v.SetFloat(7.1)
fmt.Println(v.Interface()) // 7.1
fmt.Println(x) // 7.1
- 上面的例子中,变量 v 本身并不是指针,它只是从指针衍生而来。
- 把反射应用到结构体时,常用的方式是 使用反射修改一个结构体的某些字段。
- 只要拥有结构体的地址,我们就可以修改它的字段。
type T struct {
A int
B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type() // 通过typeOfT 遍历所有的字段名字
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
// will print
0: A int = 23
1: B string = skidoo
type User struct {
Username string
}
type Admin struct {
User
title string
}
func main() {
var u Admin
t := reflect.TypeOf(u)
for i, n := 0, t.NumField(); i < n; i++ {
f := t.Field(i)
fmt.Println(f.Name, f.Type)
}
}
输出:
User main.User
title string
func main() {
u := new(Admin)
t := reflect.TypeOf(u)
if t.Kind() == reflect.Ptr { // 是指针
t = t.Elem() // 进而获取 目标类型
}
for i, n := 0, t.NumField(); i < n; i++ {
f := t.Field(i)
fmt.Println(f.Name, f.Type)
}
}
type User struct {
}
type Admin struct {
User
}
func (*User) ToString() {}
func (Admin) test() {}
func main() {
var u Admin
methods := func(t reflect.Type) {
for i, n := 0, t.NumMethod(); i < n; i++ {
m := t.Method(i)
fmt.Println(m.Name)
}
}
fmt.Println("--- value interface ---")
methods(reflect.TypeOf(u))
fmt.Println("--- pointer interface ---")
methods(reflect.TypeOf(&u))
}
输出:
--- value interface ---
test // 只有 test 方法
--- pointer interface ---
ToString
test
type User struct {
Username string
age int }
type Admin struct {
User
title string
}
func main() {
var u Admin
t := reflect.TypeOf(u)
f, _ := t.FieldByName("title")
fmt.Println(f.Name) // title
f, _ = t.FieldByName("User")
fmt.Println(f.Name) // User
f, _ = t.FieldByName("Username")
fmt.Println(f.Name) // Username
// Admin[0] -> User[1] -> age
f = t.FieldByIndex([]int{0, 1})
fmt.Println(f.Name) //age
}
type User struct {
Name string `field:"username" type:"nvarchar(20)"`
Age int `field:"age" type:"tinyint"`
}
func main() {
var u User
t := reflect.TypeOf(u)
f, _ := t.FieldByName("Name")
fmt.Println(f.Tag)
//输出: field:"username" type:"nvarchar(20)"
fmt.Println(f.Tag.Get("field")) // username
fmt.Println(f.Tag.Get("type")) // nvarchar(20)
}
var (
Int = reflect.TypeOf(0)
String = reflect.TypeOf("")
)
func main() {
c := reflect.ChanOf(reflect.SendDir, String)
fmt.Println(c) // chan<- string
m := reflect.MapOf(String, Int)
fmt.Println(m) // map[string]int
s := reflect.SliceOf(Int)
fmt.Println(s) // []int
t := struct{ Name string }{}
p := reflect.PtrTo(reflect.TypeOf(t))
fmt.Println(p) // *struct { Name string }
}
func main() {
t := reflect.TypeOf(make(chan int)).Elem()
fmt.Println(t) // int
}
type Data struct {
}
func (*Data) String() string {
return ""
}
func main() {
var d *Data
t := reflect.TypeOf(d)
it := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
fmt.Println(t.Implements(it))
}
type Data struct {
b byte
x int32 }
func main() {
var d Data
t := reflect.TypeOf(d)
fmt.Println(t.Size(), t.Align()) // 8 4
f, _ := t.FieldByName("b")
fmt.Println(f.Type.FieldAlign()) // 1
}
type User struct {
Username string
age int
}
type Admin struct {
User
title string
}
func main() {
u := &Admin{User{"Jack", 23}, "NT"}
v := reflect.ValueOf(u).Elem()
fmt.Println(v.FieldByName("title").String()) // NT
fmt.Println(v.FieldByName("age").Int()) // 23
fmt.Println(v.FieldByIndex([]int{0, 1}).Int()) // 23
}
除返回具体的 .String() .Int(), 还可返回.Interface()
.Interface() 非导出字段不能用,用CanInterface判断一下。
type User struct {
Username string
age int
}
func main() {
u := User{"Jack", 23}
v := reflect.ValueOf(u)
fmt.Println(v.FieldByName("Username").Interface())
//输出: Jack
fmt.Println(v.FieldByName("age").Interface())
//输出: panic: unexported field
// 转化成具体类型却不会 引发panic
fmt.Println(v.FieldByName("age").Int())
// 输出: 23
}
func main() {
// slice
v := reflect.ValueOf([]int{1, 2, 3})
for i, n := 0, v.Len(); i < n; i++ {
fmt.Println(v.Index(i).Int())
}
// map
fmt.Println("---------------------------")
v = reflect.ValueOf(map[string]int{"a": 1, "b": 2})
for _, k := range v.MapKeys() {
fmt.Println(k.String(), v.MapIndex(k).Int())
}
}
输出:
1
2
3
---------------------------
a1
b2
需要注意, Value某些方法没有遵循'comma ok'模式,
而是返回zero value, 要用 IsValid 判断一下.
type User struct {
Username string
age int
}
func main() {
u := User{}
v := reflect.ValueOf(u)
f := v.FieldByName("a")
fmt.Println(f.Kind(), f.IsValid()) // invalid false
}
func main() {
var p *int
var x interface{} = p
fmt.Println(x == nil) // false
v := reflect.ValueOf(p)
fmt.Println(v.Kind(), v.IsNil()) // ptr true
}
m_ptr := reflect.New( reflect.TypeOf( v ) ).Interface()
type Data struct {
}
func (*Data) Test(x, y int) (int, int) {
return x + 100, y + 100
}
func (*Data) Sum(s string, x ...int) string {
c := 0
for _, n := range x {
c += n
}
return fmt.Sprintf(s, c)
}
func info(m reflect.Method) {
t := m.Type
fmt.Println(m.Name) // 方法名
// 参数类型
for i, n := 0, t.NumIn(); i < n; i++ {
fmt.Printf(" in[%d] %v\n", i, t.In(i))
}
// 返回值类型
for i, n := 0, t.NumOut(); i < n; i++ {
fmt.Printf(" out[%d] %v\n", i, t.Out(i))
}
}
func main() {
d := new(Data)
t := reflect.TypeOf(d)
// 反射获取方法
test, _ := t.MethodByName("Test")
info(test)
sum, _ := t.MethodByName("Sum")
info(sum)
}
输出:
Test
in[0] *main.Data
in[1] int
in[2] int
out[0] int
out[1] int
Sum
in[0] *main.Data
in[1] string
in[2] []int
out[0] string