golang中reflect使用
获取ptr的大小,操作系统指针大小
const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
- uintptr(0) 将 0 转换为 uintptr 类型
- ^uintptr(0) 对 0 进行取反 : 32为 0xFFFF FFFFF FFFF FFFF 64位为 0xFFFF FFFFF FFFF FFFF FFFF FFFFF FFFF FFFF
- (^uintptr(0) >> 63) 左移 63 位 : 32 位系统为0; 64位系统为1
- 4 << (^uintptr(0) >> 63) 所以结果就是 32位系统 指针大小为4 64位系统ptr大小为8
reflect.Indirect(v) 是 v.Elem() 的升级版.
func Indirect(v Value) Value {
if v.Kind() != Ptr {
return v
}
return v.Elem()
}
// Elem()只负责解析 指针和空接口
func (v Value) Elem() Value {
k := v.kind()
switch k {
case Interface:
...
return unpackEface(eface)
case Ptr:
...
return Value{typ, ptr, fl}
}
panic(v.kind())
}
reflect.Indirect(v) 和 v.Elem(v) 区别:
如果reflect.Value
是一个指针*p,
1.1 那么v.Elem()等价于reflect.Indirect(v)
如果reflect.Value
不是指针
2.1 如果是interface, 那么reflect.Indirect(v)返回同样的值,而v.Elem()返回接口的动态的值
2.2 如果是其它值, v.Elem()会panic,而reflect.Indirect(v)返回原值
reflect包的两个重要的函数: TypeOf(i) ValueOf(i)
reflect.TypeOf(i interface{}) Type
reflect.ValueOf(i interface{}) Value
因为接收的参数是空接口interface{}
类型,因此可以接收任意类型的数据。
TypeOf()的返回值是这个接口类型对应的reflect.Type对象。通过Type提供的一些方法,就可以获得这个接口实际的静态类型。
reflect包的两个重要的类型 reflect.Type 和 reflect.Value
这两种类型reflect.Type
和reflect.Value
都提供了大量的方法让我们可以检查和操作这两种类型
共同点: 他们都有 Kind() 方法.Kind()
方法reflect.Type
和reflect.Value
都有,返回一个const 它是对象的基本类型,表示具体类型的底层类型, 共27种 如 Bool Int8 Uintptr Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer 等,而不是静态类型User
(自定义类型通过Type()获取,只有Value对象有)。
不同点: Type,Value分别对应对象的类型和值数据
Type interface{} 是一个接口:可以表示一个Go类型
reflect.Type是一个接口类型的对象,这个接口包含了很多方法,像Name(),Field(),Method()等
1. Elem() Type 方法返回 Array、Chan、Slice、Map、Ptr的基类型; 对指针类型的数据"解引用"
可用反射提取struct tag,还能自动分解,常用于ORM映射、数据验证等;
辅助判断方法Implements()、ConvertibleTo()、AssignableTo()
Value struct{} 是一个结构体:可以持有一个任意类型的值
1. Elem() Value 方法返回 Interface、Ptr;
2. Type() 方法将返回具体类型所对应的自定义类型 如 `User`
3. Interface() 方法是`reflact.ValueOf()`方法的逆,它把一个reflect.Value恢复成一个接口值,把Value中保存的类型和值的信息打包成一个接口表示并返回;如:
`y,ok := v.Interface().(float64)` // y 的类型被断言为 float64
`fmt.Println(y)`
以上可简写为这样:
`fmt.Println(v.Interface())` //fmt.Println会把它恢复出来
chan类型的反射对象:有TrySend()、TryRecv()方法;
IsNil()方法判断反射对象保存的值是否为nil;
Kind()
返回的27种类型:
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
func (elt ZeroIsNullMeddler) PostRead(fieldAddr, scanTarget interface{}) error {
sv := reflect.ValueOf(scanTarget)
fv := reflect.ValueOf(fieldAddr)
if sv.Elem().IsNil() {
// null column, so set target to be zero value
fv.Elem().Set(reflect.Zero(fv.Elem().Type()))
} else {
// copy the value that scan found
fv.Elem().Set(sv.Elem().Elem())
}
return nil
}
func (elt ZeroIsNullMeddler) PreWrite(field interface{}) (saveValue interface{}, err error) {
val := reflect.ValueOf(field)
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if val.Int() == 0 {
return nil, nil
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if val.Uint() == 0 {
return nil, nil
}
case reflect.Float32, reflect.Float64:
if val.Float() == 0 {
return nil, nil
}
case reflect.Complex64, reflect.Complex128:
if val.Complex() == 0 {
return nil, nil
}
case reflect.String:
if val.String() == "" {
return nil, nil
}
default:
return nil, fmt.Errorf("ZeroIsNullMeddler.PreWrite: unknown struct field type: %T", field)
}
return field, nil
}
list := []*model.File{}
err := meddler.QueryAll(db, &list, stmt, build.ID)
func QueryAll(db DB, dst interface{}, query string, args ...interface{}) error {
return d.ScanAll(rows, dst)
}
// ScanAll scans all sql result rows into a slice of structs.
// dst should be a pointer to a slice of the appropriate type.
func (d *Database) ScanAll(rows *sql.Rows, dst interface{}) error {
// make sure we always close rows
defer rows.Close()
// make sure dst is an appropriate type
dstVal := reflect.ValueOf(dst)
if dstVal.Kind() != reflect.Ptr || dstVal.IsNil() {
return fmt.Errorf("ScanAll called with non-pointer destination: %T", dst)
}
sliceVal := dstVal.Elem()
if sliceVal.Kind() != reflect.Slice {
return fmt.Errorf("ScanAll called with pointer to non-slice: %T", dst)
}
sliceVal.Elem()
ptrType := sliceVal.Type().Elem()
if ptrType.Kind() != reflect.Ptr {
return fmt.Errorf("ScanAll expects element to be pointers, found %T", dst)
}
eltType := ptrType.Elem()
if eltType.Kind() != reflect.Struct {
return fmt.Errorf("ScanAll expects element to be pointers to structs, found %T", dst)
}
// gather the results
for {
// create a new element
eltVal := reflect.New(eltType) // 通过反射创建Struct对象,返回值为reflect.Value类型
elt := eltVal.Interface() // 将Struct对象转为接口类型
// scan it
if err := d.scanRow(data, rows, elt, columns); err != nil {
if err == sql.ErrNoRows {
return nil
}
return err
}
// add to the result slice
sliceVal.Set(reflect.Append(sliceVal, eltVal))
}
}
reflect包是golang中很重要的一个包,实现了在运行时允许程序操纵任意类型对象的功能。可以看下文档简单了解一下。
在reflect中,最重要的是Value类,只有先获取到一个对象或者变量的Value对象后,我们才可以对这个对象或者变量进行更进一步的分析和处理。我们可以使用reflect.ValueOf()方法获取Value对象。
1.从interface{}接口变量中获取value和type信息
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type: ", reflect.TypeOf(x)) //type: float64
fmt.Println("type: ", reflect.ValueOf(x)) //type: 3.4
}
2.从Value中获取接口信息
类型为”relfect.Value”变量,通过下面的方法可以获得接口变量:
func (v Value) Interface() interface{}
当收到一个类型为reflect.Value类型的变量时,用下面方式将它转换对应的接口变量,然后进行类型判断:
y := v.Interface().(float64)
之后就可以使用y的成员和方法。
3.通过reflect.Value设置实际变量的值
reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值。
例如:
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
v := p.Elem()
fmt.Println("type of p:", v.Type())
fmt.Println("settability of p:", v.CanSet())
v.SetFloat(77)
传入的是 *float64
,需要用p.Elem()获取所指向的Value。v.CantSet()输出的是true,因此可以用v.SetFloat()修改x的值。
收到reflect.Value变量后
如果得到了一个类型为reflect.Value的变量,可以通过下面的方式,获得变量的信息。
如果知道v的真实类型,可先转换成interface{},然后转化成对应的类型:r := v.Interface().(已知的类型)
除了interface{},还可以转换成其它类型:
func (v Value) Bool() bool
func (v Value) Bytes() []byte
func (v Value) Int() int64
func (v Value) Uint() uint64
...
如果不知道v的真实类型,获取它的Type,然后遍历Type的Field,和v的Field:
t := v.Type()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, t.Field(i).Name, f.Type(), f.Interface())
}
1.获取对象或者变量的类型 [Value.Type()和Value.Kind()] 区别
Value.Type() 和 Value.Kind() 这两个方法都可以获取对象或者变量的类型,如果是变量的话,使用这两个方法获取到的类型都是一样,差别是结构体对象
举个例子看一下:
var i int
value := reflect.ValueOf(i)
log.Println(value.Type()) //输出:int
log.Println(value.Kind()) //输出:int
type S struct {
a string
}
var s S
value2 := reflect.ValueOf(s) // 使用ValueOf()获取到结构体的Value对象
log.Println(value2.Type()) //输出:S
log.Println(value2.Kind()) //输出:struct
变量i使用kind和type两个方法都输出了int,而结构体s的Type()方法输出了S,Kind()方法输出了struct,由此可以总结如下,如果你想拿到结构体里面定义的变量信息的时候,使用Type(f)方法。如果只是相判断是否是结构体时,就使用Kind()
2.获取变量的值,修改变量值
获取变量的值使用value.Interface()方法,该方法会返回一个value的值,不过类型是interface。给变量赋值需要先判断该变量的类型,使用之前提到过的Value.Kind()方法,如果变量的类型是reflect.Int,我们就可以使用Value.SetInt()方法给变量赋值。下面是一个例子:
var i int = 1
// 获取Value,这里注意,如果你要改变这个变量的话,需要传递变量的地址
value := reflect.ValueOf(&i)
// value是一个指针,这里获取了该指针指向的值,相当于value.Elem()
value = reflect.Indirect(value)
// Interface是获取该value的值,返回的是一个interface对象
log.Println(value.Interface()) // 输出:1
// 把变量i的值设为2
if value.Kind() == reflect.Int {
value.SetInt(2)
}
log.Println(value.Interface()) // 输出:2
给结构体对象中的成员变量赋值的方法:
type S struct {
A string // 注意:只有大写开头的成员变量可以Set
}
s := S{"x"}
value := reflect.ValueOf(&s)
value = reflect.Indirect(value)
//value是结构体s,所以打印出来的是整个结构体的信息
log.Println(value.Interface()) //输出: {x}
f0 := value.FieldByName("A") //获取结构体s中第一个元素a
log.Println(f0) // 输出: x
if f0.Kind() == reflect.String {
if f0.CanSet() {
f0.SetString("y")
}
}
log.Println(f0) // 输出: y
log.Println(value.Interface()) //输出: {y}
结构体这里需要注意的是,只有公有的成员变量可以被reflect改变值,私有的变量是无法改变值得。
3.获取结构体成员变量的tag信息
由于golang变量大小写和公有私有息息相关,所以码农门很难按照自己的意愿来定义变量名。于是golang提供了tag机制,来给变量提供一个标签,这个标签可以作为一个别名,来给一些存储结构来获取结构体变量名字使用。下面是一个获取结构体成员变量tag信息的例子:
type S struct {
A string `json:"tag_a"`
}
s := S{}
value := reflect.ValueOf(&s)
value = reflect.Indirect(value)
//获取结构体s的类型S
vt := value.Type()
//获取S中的A成员变量
f, _ := vt.FieldByName("A")
//获取成员变量A的db标签
log.Println(f.Tag.Get("json")) //输出: tag_a
reflect库的godoc在http://golang.org/pkg/reflect/
reflect包有两个数据类型我们必须知道,一个是Type,一个是Value。
Type就是定义的类型的一个数据类型,Value是值的类型
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
name string
}
func (this *MyStruct) GetName(str string) string {
this.name = str
return this.name
}
func main() {
// 备注: reflect.Indirect -> 如果是指针则返回 Elem()
// 首先,reflect包有两个数据类型我们必须知道,一个是Type,一个是Value。
// Type就是定义的类型的一个数据类型,Value是值的类型
// 对象
s := "this is string"
// 获取对象类型 (string)
fmt.Println(reflect.TypeOf(s))
// 获取对象值 (this is string)
fmt.Println(reflect.ValueOf(s))
// 对象
var x float64 = 3.4
// 获取对象值 (<float64 Value>)
fmt.Println(reflect.ValueOf(x))
// 对象
a := &MyStruct{name: "nljb"}
// 返回对象的方法的数量 (1)
fmt.Println(reflect.TypeOf(a).NumMethod())
// 遍历对象中的方法
for m := 0; m < reflect.TypeOf(a).NumMethod(); m++ {
method := reflect.TypeOf(a).Method(m)
fmt.Println(method.Type) // func(*main.MyStruct) string
fmt.Println(method.Name) // GetName
fmt.Println(method.Type.NumIn()) // 参数个数
fmt.Println(method.Type.In(1)) // 参数类型
}
// 获取对象值 (<*main.MyStruct Value>)
fmt.Println(reflect.ValueOf(a))
// 获取对象名称
fmt.Println(reflect.Indirect(reflect.ValueOf(a)).Type().Name())
// 参数
i := "Hello"
v := make([]reflect.Value, 0)
v = append(v, reflect.ValueOf(i))
// 通过对象值中的方法名称调用方法 ([nljb]) (返回数组因为Go支持多值返回)
fmt.Println(reflect.ValueOf(a).MethodByName("GetName").Call(v))
// 通过对值中的子对象名称获取值 (nljb)
fmt.Println(reflect.Indirect(reflect.ValueOf(a)).FieldByName("name"))
// 是否可以改变这个值 (false)
fmt.Println(reflect.Indirect(reflect.ValueOf(a)).FieldByName("name").CanSet())
// 是否可以改变这个值 (true)
fmt.Println(reflect.Indirect(reflect.ValueOf(&(a.name))).CanSet())
// 不可改变 (false)
fmt.Println(reflect.Indirect(reflect.ValueOf(s)).CanSet())
// 可以改变
// reflect.Indirect(reflect.ValueOf(&s)).SetString("jbnl")
fmt.Println(reflect.Indirect(reflect.ValueOf(&s)).CanSet())
}
获取 Struct 对象的 Tag
type Home struct {
i int `json:"100"`
j int `json:"200"`
}
func main() {
home := new(Home)
home.i = 5
rcvr := reflect.ValueOf(home)
typ := reflect.Indirect(rcvr).Type()
fmt.Println(typ.Kind().String())
x := typ.NumField()
for i := 0; i < x; i++ {
json := typ.Field(i).Tag.Get("json")
fmt.Println(json)
}
}
反射使用案例
package server
import (
"fmt"
"net/http"
"reflect"
"strings"
)
type Server struct {
name string
rcvr reflect.Value
typ reflect.Type
methods map[string]*Method
}
type Method struct {
method reflect.Method
json bool
}
func NewServer() *Server {
server := new(Server)
server.methods = make(map[string]*Method)
return server
}
func (this *Server) Start(port string) error {
return http.ListenAndServe(port, this)
}
func (this *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for mname, mmethod := range this.methods {
if strings.ToLower("/"+this.name+"."+mname) == r.URL.Path {
if mmethod.json {
returnValues := mmethod.method.Func.Call(
[]reflect.Value{this.rcvr, reflect.ValueOf(w), reflect.ValueOf(r)})
content := returnValues[0].Interface()
if content != nil {
w.WriteHeader(500)
...
}
} else {
mmethod.method.Func.Call(
[]reflect.Value{this.rcvr, reflect.ValueOf(w), reflect.ValueOf(r)})
}
}
}
}
/*
func (this *Hello) JsonHello(r *http.Request) {}
func (this *Hello) Hello(w http.ResponseWriter, r *http.Request) {}
*/
func (this *Server) Register(rcvr interface{}) error {
this.typ = reflect.TypeOf(rcvr)
this.rcvr = reflect.ValueOf(rcvr)
this.name = reflect.Indirect(this.rcvr).Type().Name()
if this.name == "" {
return fmt.Errorf("no service name for type ", this.typ.String())
}
for m := 0; m < this.typ.NumMethod(); m++ {
method := this.typ.Method(m)
mtype := method.Type
mname := method.Name
if strings.HasPrefix(mname, "Json") {
if mtype.NumIn() != 2 {
return fmt.Errorf("method %s has wrong number of ins: %d", mname, mtype.NumIn())
}
arg := mtype.In(1)
if arg.String() != "*http.Request" {
return fmt.Errorf("%s argument type not exported: %s", mname, arg)
}
this.methods[mname] = &Method{method, true}
} else {
if mtype.NumIn() != 3 {
return fmt.Errorf("method %s has wrong number of ins: %d", mname, mtype.NumIn())
}
reply := mtype.In(1)
if reply.String() != "http.ResponseWriter" {
return fmt.Errorf("%s argument type not exported: %s", mname, reply)
}
arg := mtype.In(2)
if arg.String() != "*http.Request" {
return fmt.Errorf("%s argument type not exported: %s", mname, arg)
}
this.methods[mname] = &Method{method, false}
}
}
return nil
}
// ... //
type Hello struct {
}
func (this *Hello) Print(w http.ResponseWriter, r *http.Request) map[string]interface{} {
w.Write([]byte("print"))
return nil
}
func (this *Hello) Hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
}
func (this *Hello) JsonHello(r *http.Request) {
}
func main() {
server := NewServer()
fmt.Println(server.Register(new(Hello)))
server.Start(":8080")
}