前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go reflect 学习

go reflect 学习

作者头像
solate
发布2019-07-22 17:40:51
6600
发布2019-07-22 17:40:51
举报
文章被收录于专栏:solate 杂货铺solate 杂货铺

reflect

空接口 interface{} (重要)

理解接口包含的(value, type)对很重要

Go的接口都是静态类型化的:一个接口类型变量总是保持同一个静态类型,即使在运行时它保存的值的类型发生变化,这些值总是满足这个接口。

接口的表示

一个接口存储一个pair:赋值给这个接口变量的具体值,以及这个值的类型描述符.

代码语言:javascript
复制
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)

虽然 io.Reader只提供了 Read 方法,但是我们依然可以获得到具体的 (tty, *os.File) 对, 所以任然可以使用这个值得全部信息.

比如我们可以将其断言成 io.Writer

代码语言:javascript
复制
var w io.Writer
w = r.(io.Writer)

// w 包含的依然是 (tty, *os.File)

同样可以再赋值给空接口 interface{}

代码语言:javascript
复制
var empty interface{}
empty = w

//empty 依然是 (tty, *os.File)

所以我们总是可以获得这个具体的值,这样就为反射做好准备.

Type 和 Value

使用反射,首先要知道这两个的区别

  • Type : 反射的数据类型
  • Value : 具体的值
代码语言:javascript
复制
var str string = "this is a string"

Type => string
Value => this is a string


源码可以看到:

//Type 是个接口
type Type interface{
    //一些方法
}
//Value 是个 struct
type Value struct {
    //属性
}


//源码
//rtype 实现了 Type 接口
type rtype struct {
    //实际使用的时候用的是这个
}

TypeOf 和 ValueOf

** TypeOf 和 ValueOf 是获取 Type 和 Value 的方法**

代码语言:javascript
复制
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type

当我们调用reflect.Typeof(x)的时候,x首先被保存到一个空接口中,这个空接口然后被作为参数传递。reflect.Typeof 会把这个空接口拆包(unpack)恢复出类型信息。

例如

代码语言:javascript
复制
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))

    fmt.Println("value:", reflect.ValueOf(x)) //Valueof方法会返回一个Value类型的对象
}
代码语言:javascript
复制
type: float64
value: 3.4


//文档 上写的是这样的, 也就是表示值得意思
value: <float64 Value>

从Value到达Type是通过Value中定义的某些方法来实现的

Value 类型有 Type() 方法可以返回这个value的 Type的类型

(这个方法返回的是值的静态类型即static type,也就是说如果定义了type MyInt int64,那么这个函数返回的是MyInt类型而不是int64,看后面那个Kind方法就可以理解了) Type和Value都有一个Kind方法可以返回一个常量用于指示一个项到底是以什么形式(也就是底层类型即underlying type,继续前面括号里提到的,Kind返回的是int64而不是MyInt)

代码语言:javascript
复制
package main

import (
	"fmt"
	"reflect"
)

type MyInt int64

func main() {
	var x MyInt = 3
	fmt.Println("type:", reflect.TypeOf(x))


	fmt.Println("value:", reflect.ValueOf(x)) //Valueof方法会返回一个Value类型的对象

	fmt.Println(reflect.ValueOf(x).Type())


	fmt.Println(reflect.TypeOf(x).Kind())
	fmt.Println(reflect.ValueOf(x).Kind())

}



//结果
type: main.MyInt
value: 3
main.MyInt
int64
int64
原则1:

Reflection goes from interface value to reflection object.

keep the API simple

官方:第一个性质是,为了保持API简单,Value的”setter”和“getter”类型的方法操作的是可以包含某个值的最大类型

其实说的就是 如 reflect包中 Int(), Uint(), Float()等方法 返回的都是最大的那个如int64, int64, float64等. SetInt(int64), SetFloat(float64)等.

代码语言:javascript
复制
package main

import (
	"fmt"
	"reflect"
)


func main() {

	var x uint8 = 'x'
	v := reflect.ValueOf(x)
	fmt.Println("type:", v.Type())                            // uint8.
	fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
	x = uint8(v.Uint())// v.Uint returns a uint64.看到啦嘛?这个地方必须进行强制类型转换!

	fmt.Println(reflect.TypeOf(v.Uint())) //

}


//结果
type: uint8
kind is uint8:  true
uint64

官方:第二个性质是,反射对象(reflection object)的Kind描述的是底层类型(underlying type),而不是静态类型(static type)

代码语言:javascript
复制
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

// v.Kind() 为 reflect.Int

即使x的静态类型是MyInt而不是int。换句话说,Kind不能将一个int从一个MyInt中区别出来,但是Type能做到
使用value.Type() 结果是 MyInt
原则2: ( value.Interface() )

Reflection goes from reflection object to interface value

也就是说反射对象可以逆反射成接口

给定一个reflect.Value,我们能用Interface方法把它恢复成一个接口值;效果上就是这个Interface方法把类型和值的信息打包成一个接口表示并且返回结果

简单说:Interface方法是Valueof函数的逆

代码语言:javascript
复制
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}
代码语言:javascript
复制
package main

import (
	"fmt"
	"reflect"
)


func main() {

	var x float64 = 3.2
	v := reflect.ValueOf(x)
	y := v.Interface().(float64) // y will have type float64.
	fmt.Println(y)  //3.2

	//fmt.Println可以处理interface{}, 所以可以直接
	fmt.Println(v.Interface())   // 3.2


	//因为是float, 所以也可以用printf,不是不行
	 fmt.Printf("value is %7.1e\n", v.Interface())  //value is 3.2e+00

}
原则3: ( CanSet(), Elem() )

To modify a reflection object, the value must be settable.

修改反射对象,值必须是可settable的

这段代码会发生panic

代码语言:javascript
复制
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.


//panic: reflect.Value.SetFloat using unaddressable value

问题不是不能寻址, 而是出在v不是settable的.

Settability是Value的一条性质,而且不是所有的Value都具备这条性质.

代码语言:javascript
复制
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())

//settability of v: false

也就是我们 reflect.ValueOf创造出的接口值v,只是x的副本,所以不能赋值.

就像我们使用函数f(x), 传入的x是个副本,我们不能用传入的x修改原来的值.

除非我们传入的是f(&x)才可以修改值,同样反射如果想修改值,就要将要修改值得指针传入反射库.

代码语言:javascript
复制
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.注意这里哦!我们把x地址传进去了!
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())

//结果
type of p: *float64
settability of p: false

我们传入了x的指针,我们想设置的是*p,为了得到他我们使用 value.Elem()来获得具体的

代码语言:javascript
复制
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

reflection Values need the address of something in order to modify what they represent 反射Values为了修改它们所表示的东西必须要有这些东西的地址

struct

使用struct 进行反射,最大的问题就是在实例化的时候, 有可能是直接赋值struct,而有一些是new出来的 表示的是指针, 这样在反射方法的时候就会有一些问题.

代码语言:javascript
复制
type T struct {
    A int
    B string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem() //这里传入的是&t,所以是可设置的, 如果是t,就不可以设置了
typeOfT := s.Type()//把s.Type()返回的Type对象复制给typeofT,typeofT也是一个反射。
for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)//迭代s的各个域,注意每个域仍然是反射。
    fmt.Printf("%d: %s %s = %v\n", i,
        typeOfT.Field(i).Name, f.Type(), f.Interface())//提取了每个域的名字
}



//结果
0: A int = 23
1: B string = skidoo

** 这里T中首字母都是大写的(可导出的)**,只有可导出的域才是settable的.

因为传入的是指针,所以是可修改的,那么我们可以修改这些值.

代码语言:javascript
复制
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)

t is now {77 Sunset Strip}

或者这样也是一样的

代码语言:javascript
复制
t := new(T)
t.A = 23
t.B = "skidoo"
s := reflect.ValueOf(t).Elem() //因为new完表示的就是指针

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • reflect
    • 空接口 interface{} (重要)
      • 接口的表示
    • Type 和 Value
      • TypeOf 和 ValueOf
      • struct
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档