reflect即反射。对于C++程序员来说比较陌生,对于Java或是C#程序员来说理解反射就易如反掌了。golang中为我们提供了reflect包用于反射。
package reflect
接下来就要介绍golang中的reflect package了。
reflect包有两个数据类型,一个是Type,一个是Value。
1.Type就是定义的类型的一个数据类型
2.Value是值的类型
反射是一种检查存储在接口变量中的<值,类型>对的机制,借助go反射包提供的reflect.TypeOf和reflect.ValueOf可以方便的访问到一个接口值的reflect.Type和reflect.Value部分,从而可进一步得到这个接口的结构类型和对其进行值的修改操作。
Type
type Type interface {
// Methods applicable to all types.
// Align returns the alignment in bytes of a value of
// this type when allocated in memory.
Align() int
// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign() int
// Method returns the i'th method in the type's method set.
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
Method(int) Method
// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)
// NumMethod returns the number of exported methods in the type's method set.
NumMethod() int
// Name returns the type's name within its package.
// It returns an empty string for unnamed types.
Name() string
// PkgPath returns a named type's package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
// If the type was predeclared (string, error) or unnamed (*T, struct{}, []int),
// the package path will be the empty string.
PkgPath() string
// Size returns the number of bytes needed to store
// a value of the given type; it is analogous to unsafe.Sizeof.
Size() uintptr
// String returns a string representation of the type.
// The string representation may use shortened package names
// (e.g., base64 instead of "encoding/base64") and is not
// guaranteed to be unique among types. To test for type identity,
// compare the Types directly.
String() string
// Kind returns the specific kind of this type.
Kind() Kind
// Implements reports whether the type implements the interface type u.
Implements(u Type) bool
// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
Comparable() bool
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// ChanDir returns a channel type's direction.
// It panics if the type's Kind is not Chan.
ChanDir() ChanDir
// IsVariadic reports whether a function type's final input parameter
// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
// implicit actual type []T.
//
// For concreteness, if t represents func(x int, y ... float64), then
//
// t.NumIn() == 2
// t.In(0) is the reflect.Type for "int"
// t.In(1) is the reflect.Type for "[]float64"
// t.IsVariadic() == true
//
// IsVariadic panics if the type's Kind is not Func.
IsVariadic() bool
// Elem returns a type's element type.
// It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
Elem() Type
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
FieldByIndex(index []int) StructField
// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
FieldByName(name string) (StructField, bool)
// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
// and then the fields in any anonymous structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go's handling of name lookup in
// structs containing anonymous fields.
FieldByNameFunc(match func(string) bool) (StructField, bool)
// In returns the type of a function type's i'th input parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumIn()).
In(i int) Type
// Key returns a map type's key type.
// It panics if the type's Kind is not Map.
Key() Type
// Len returns an array type's length.
// It panics if the type's Kind is not Array.
Len() int
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int
// NumIn returns a function type's input parameter count.
// It panics if the type's Kind is not Func.
NumIn() int
// NumOut returns a function type's output parameter count.
// It panics if the type's Kind is not Func.
NumOut() int
// Out returns the type of a function type's i'th output parameter.
// It panics if the type's Kind is not Func.
// It panics if i is not in the range [0, NumOut()).
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
部分函数介绍
获取 t 类型的字符串描述。
func (t *rtype) String() string
获取 t 类型在其包中定义的名称,未命名类型则返回空字符串。
func (t *rtype) Name() string
获取 t 类型所在包的名称,未命名类型则返回空字符串。
func (t *rtype) PkgPath() string
获取 t 类型的类别。
func (t *rtype) Kind() reflect.Kind
switch typ.Kind() {
case reflect.Ptr:
fmt.Println("it's ptr")
case reflect.Int:
...
}
获取 t 类型的方法数量。
func (t *rtype) NumMethod() int
type Demo struct {
Param string
}
func (this *Demo) Hello() {
fmt.Println("hello")
}
func Test1(t *testing.T){
demo:=&Demo{
Param:"1234",
}
typ:= reflect.TypeOf(demo)
fmt.Println("NumMethod:",typ.NumMethod())
}
$NumMethod:1
根据索引获取 t 类型的方法,如果方法不存在,则 panic。
func (t *rtype) Method(int) reflect.Method
type Demo struct {
Param string
}
func (this *Demo) Hello() {
fmt.Println("hello")
}
func Test1(t *testing.T){
demo:=&Demo{
Param:"1234",
}
typ:= reflect.TypeOf(demo)
method:=typ.Method(0)
fmt.Println("方法名:",method.Name)
}
$方法名: Hello
func ValueOf
func ValueOf(i interface{}) Value
获取对象类型和值
type Demo struct {
Param1 string
Param2 string
}
func (this *Demo) Hello() {
fmt.Println("hello")
}
func Test1(t *testing.T){
demo:=&Demo{
Param1:"1",
Param2:"2",
}
value:= reflect.ValueOf(demo)
name:= reflect.Indirect(value).Type().Name()
fmt.Println("值得名字:",name)
d:=value.Interface().(*Demo)
fmt.Println(d)
}
$值得名字: Demo
$&{1 2}
通过反射修改对象
方法一:
demo:=&Demo{
Param1:"1",
Param2:"2",
}
value:= reflect.ValueOf(demo)
p:=value.Interface().(*Demo)
p.Param1="1_1"
p.Param2="2_2"
fmt.Println(p)
方法二:
demo:=&Demo{
Param1:"1",
Param2:"2",
}
value:= reflect.ValueOf(demo)
e:=value.Elem()
p:=e.Addr().Interface().(*Demo)
p.Param1="1_1"
p.Param2="2_2"
fmt.Println(p)
反射例子
一般的RPC框架中都会用到反射机制,比如服务提供端使用反射来发布服务,服务消费方调用的时候内部也会用到反射。
首先定义服务类Hello
type CmdIn struct {
Param string
}
func (this *CmdIn) String() string {
return fmt.Sprintf("[CmdIn Param is %s]",this.Param)
}
type CmdOut struct {
Result int
Info string
}
func (this *CmdOut) String() string {
return fmt.Sprintf("Result[%d] Info[%s]",this.Result,this.Info)
}
//服务提供类Hello
type Hello struct {
Param string
}
//服务函数Test1
func (this *Hello) Test1(ctx context.Context, in *CmdIn, out *CmdOut) error {
fmt.Println("接收到服务消费方输入:",in.Param)
out.Result=12345678
out.Info="我已经吃饭了!"
return nil
}
Hello服务类提供服务函数Test1,其中打印消费端传来的参数,然后返回信息给消费端
反射框架类
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfContext = reflect.TypeOf((*context.Context)(nil)).Elem()
type MethodType struct {
method reflect.Method
ArgType reflect.Type
ReplyType reflect.Type
}
type Reflect struct {
Name string
Rcvr reflect.Value
Typ reflect.Type
Method map[string]*MethodType
}
func (this *Reflect) Setup(rcvr interface{}, name string) {
this.Typ = reflect.TypeOf(rcvr)
this.Rcvr = reflect.ValueOf(rcvr)
this.Name = reflect.Indirect(this.Rcvr).Type().Name()
this.Method = suitableMethods(this.Typ, true)
}
func (this *Reflect) Call(ctx context.Context,service string,method string,in *CmdIn,out *CmdOut) error {
mtype := this.Method[method]
returnValues := mtype.method.Func.Call([]reflect.Value{this.Rcvr, reflect.ValueOf(ctx), reflect.ValueOf(in), reflect.ValueOf(out)})
errInter := returnValues[0].Interface()
if errInter != nil {
return errInter.(error)
}else{
return nil
}
}
func suitableMethods(typ reflect.Type, reportErr bool) map[string]*MethodType {
methods := make(map[string]*MethodType)
for m := 0; m < typ.NumMethod(); m++ {
method := typ.Method(m)
mtype := method.Type
mname := method.Name
if method.PkgPath != "" {
continue
}
if mtype.NumIn() != 4 {
if reportErr {
fmt.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
}
continue
}
ctxType := mtype.In(1)
if !ctxType.Implements(typeOfContext) {
if reportErr {
fmt.Println("method", mname, " must use context.Context as the first parameter")
}
continue
}
argType := mtype.In(2)
if !isExportedOrBuiltinType(argType) {
if reportErr {
fmt.Println(mname, "parameter type not exported:", argType)
}
continue
}
// Third arg must be a pointer.
replyType := mtype.In(3)
if replyType.Kind() != reflect.Ptr {
if reportErr {
fmt.Println("method", mname, "reply type not a pointer:", replyType)
}
continue
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
if reportErr {
fmt.Println("method", mname, "reply type not exported:", replyType)
}
continue
}
// Method needs one out.
if mtype.NumOut() != 1 {
if reportErr {
fmt.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
}
continue
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
if reportErr {
fmt.Println("method", mname, "returns", returnType.String(), "not error")
}
continue
}
methods[mname] = &MethodType{method: method, ArgType: argType, ReplyType: replyType}
}
return methods
}
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return isExported(t.Name()) || t.PkgPath() == ""
}
func isExported(name string) bool {
rune, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(rune)
}
服务提供端发布服务(服务注册)
hello:=&Hello{}
reflect:=&Reflect{}
//将hello类注册到服务框架中
reflect.Setup(hello,"Hello")
服务消费端调用
hello:=&Hello{}
reflect:=&Reflect{}
reflect.Setup(hello,"Hello")
fmt.Println("发起消费方调用")
//定义输入参数
in:=&CmdIn{
Param:"吃饭了吗?",
}
//定义输出参数
out:=&CmdOut{}
//消费方访问服务提供方hello函数Test1
err:=reflect.Call(context.Background(),"Hello","Test1",in,out)
if err!=nil {
fmt.Println(err)
}else{
fmt.Println("接收到服务提供方返回:",out)
}
服务消费端并不是直接调用hello.Test1函数,而是通过reflect.Call方式调用,传入服务名Hello和需要访问的函数Test1。