Go(Golang)_08_反射

@

反射

反射(Reflection):在编译不确定对象的数据类型的前提下,操作对象的机制

1)对象在编译器期间会被转换为内存地址,不会被写入可执行部分;

2)Go语言中的反射功能由reflect包提供;


reflect

reflect包:Go语言中提供反射功能的包

1)reflect包中提供两种自定义数据类型:Type(接口)、Value(结构体)

2)Type和Value分别对应多种方法可操作指定对象;


reflect.TypeOf()函数:返回反射对象(Type类型)代表指定对象数据类型

1)调用格式:reflect.TypeOf(对象)

2)其形参数数据类型为“interface{}”(可接收任意数据类型的对象);

3)reflect包中重新定义数据类型(调用格式:reflect.数据类型),如下:

type Kind uint

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
)

reflect.ValueOf()函数:返回指定对象的值(未定义,则返回nil)

1)调用格式:reflcet.ValueOf(对象)

2)返回值的数据类型(Value类型)不同于原数据类型;

3)其形参数数据类型为“interface{}”(可接收任意数据类型的对象);


如:通过reflect.TypeOf()和reflect.ValueOf()函数输出对象的数据类型和值

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

func main() {
    v := 3

    num2 := reflect.TypeOf(v)
    fmt.Println(num2)
    fmt.Println(num2.String())	//string()方法也可输出其数据类型

    num1 := reflect.ValueOf(v)
    fmt.Println(num1)
}

2)运行结果;
在这里插入图片描述


其他常用函数:

函数名 说明
reflect.PtrTo(反射对象) 返回该反射对象代表类型的 指针类型形式
(返回值类型为Type)
reflect.SliceOf(反射对象) 返回该反射对象代表类型的 切片类型形式
(返回值类型为Type)
reflect.MapOf(反射对象1,反射对象2) 返回以反射对象1和2代表 键和值的map类型形式
(返回值类型为Type)

如:通过上述函数返回整数类型的其他类型形式

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var num int = 3
    NumType := reflect.TypeOf(num)

    NumPtr := reflect.PtrTo(NumType)
    NumSlice := reflect.SliceOf(NumType)
    NumMap := reflect.MapOf(NumPtr, NumSlice)

    fmt.Println(num, NumType, NumPtr, NumSlice, NumMap)
}

2)运行结果;
在这里插入图片描述


Type

Type(接口数据类型):reflect包中Type接口定义较多函数/方法以操作对象

1)Type接口中定义方法的原型如下:返回该反射对象的自定义数据类型

type Type interface {
    Kind()    Kind   // 返回该反射对象底层数据类型
	Name()    string // 返回该反射对象的自定义数据类型
    PkgPath() string // 返回该反射对象代表类型所在的包路径
	String()  string // 以字符串形式返回底层数据类型
	// 若比较两个类型是否相等,用Type类型比较

    Size()       uintptr // 返回要保存一个该类型的值需要多少字节
    Align()      int     // 返回当从内存中申请一个该类型值时,会对齐的字节数
    FieldAlign() int     // 返回当该类型作为结构体的字段时,会对齐的字节数

	Implements(u Type)    bool // 若该类型实现了u代表的接口,会返回真
	AssignableTo(u Type)  bool // 若该类型的值可以赋值给u代表的类型,返回真
	ConvertibleTo(u Type) bool // 若该类型的值可以转换为u代表的类型,返回真
	// 返回该类型的字位数

	Bits()    int     // 返回代表内置类型的字位数
    Len()     int     // 返回代表数组类型的长度
	Elem()    Type    // 返回代表数据集合的元素类型
	Key()     Type    // 返回代表map类型的键类型
	ChanDir() ChanDir // 返回代表通道类型的方向

	NumField()                int           // 返回代表结构体类型含有的字段数(包括匿名字段)
	Field(i int)              StructField   // 返回代表结构体类型的第i个字段
	FieldByIndex(index []int) StructField   // 通过索引指定的嵌套字段类型

	FieldByName(name string) (StructField, bool) // 返回名为name的字段
	// 会同时匹配匿名字段和子字段,bool类型代表是否匹配成功

	FieldByNameFunc(match func(string) bool) (StructField, bool)
	// 返回第一个字段名满足函数match的字段,bool类型代表是否匹配成功

	IsVariadic() bool   // 判断函数的最后一个参数是否为“...”形式
    NumIn()      int    // 返回代表函数类型所包含的参数个数
    In(i int)    Type   // 返回代表函数类型的第i个参数
    NumOut()     int    // 返回代笔函数类型的返回值个数
	Out(i int)   Type   // 返回代表函数类型的第i个返回值

    NumMethod() int    // 返回反射对象所包含的方法数(包括匿名字段所带的)
	Method(int) Method // 返回反射对象包含方法中的第i个方法
	// 所包含的方法安装第一个字母的ASCII码值顺序排列

	MethodByName(string) (Method, bool) // 返回名为string的方法,bool类型代表是否匹配成功
}

普通数据类型的常用方法如下:

方法名 说明
Kind() 返回该反射对象的底层数据类型
Name() 返回该反射对象的自定义数据类型
(无,则返回空字符串)
Elem() 返回指针指向对象
(指针的数据类型均为ptr)

//常使用Elem()方法间接修改值传递的反射对象


如:通过上述方法分别判断对象的数据类型

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

type Myint int

type Mycat struct{}

func main() {
    var num Myint = 0
    NumType := reflect.TypeOf(num)
    fmt.Println(NumType.Name(), NumType.Kind())

    cat := Mycat{}
    CatType := reflect.TypeOf(cat)
    fmt.Println(CatType.Name(), CatType.Kind())

    CatPtr := &cat
    PtrType := reflect.TypeOf(CatPtr)
    fmt.Println(PtrType.Name(), PtrType.Kind(), PtrType.Elem())
}

2)运行结果;
在这里插入图片描述


结构体类型关于字段的常用方法如下(非结构体类型调用会产生宕机):

方法名 说明
Field(N) 返回该结构体的第N个字段
(以StructField结构体的形式返回)
NumField() 返回该结构体的字段数量
FieldByName(字符串) 检测结构体是否包含指定字符串的字段

StructField自定义的结构体类型所含字段如下:

type StructField struct {
    Name      string    // 字段名
    PkgPath   string    // 非导出字段的包路径
    Type      Type      // 字段反射类型
    Tag       StructTag // 字段标签
    Offset    uintptr   // 字段在结构体中相对偏移
    Index     []int     // Type.FieldByIndex中返回的索引值
    Anonymous bool      // 是否为匿名字段
}


如:通过上述方法获取结构体的数据信息

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

type Mycat struct {
    Name string
    Type int `json:"type" id:"100"`    //带有结构体tag的字段
}

func main() {
    cat := Mycat{Name: "mimi", Type: 1}
    CatType := reflect.TypeOf(cat)

    for i := 0; i < CatType.NumField(); i++ {
        fieldType := CatType.Field(i)
        fmt.Println(fieldType.Name, fieldType.Tag)
    }

    if whatType, ok := CatType.FieldByName("Type"); ok {
        fmt.Println(whatType.Tag.Get("json"), whatType.Tag.Get("id"))
    }	//Tag字段的Get()方法可指定键以获得值
}

2)运行结果;
在这里插入图片描述



结构体类型关于方法的常用方法如下(非结构体类型调用会产生宕机):

方法名 说明
NumMethod() 返回包含的方法总数
Method(N) 返回第N个方法
(以Method结构体的形式返回)
MethodByName(字符串) 检测是否包含指定方法

Method自定义结构体类型所含字段如下:

type Method struct {
    Name    string  // 方法名
    PkgPath string	// 非导出字段的包路径
    Type    Type	// 方法类型
    Func    Value	// 方法值
    Index   int		// Type.Method的索引
}

如:通过反射对象获取对象的方法信息

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

type Animal struct {
    Name string
    Age  int
}

func (a Animal) Cmeth() {}
func (a Animal) Bmeth() {}
func (a Animal) Ameth() {}

func main() {
    cat := Animal{"mimi", 12}
    CatType := reflect.TypeOf(cat)

    for i := 0; i < CatType.NumMethod(); i++ {
        fmt.Println(CatType.Method(i).Name)
        if CatType.Method(i).Name == "Ameth" {
            fmt.Println("This is A")
        }
    }

    fmt.Println(cat)
}

2)运行结果;
在这里插入图片描述


Value

Value(结构体数据类型):reflect包中Value定义较多函数/方法以操作对象

1)Value结构体中定义的方法只能由对应的数据类型调用,否则会产生宕机;

2)Value结构体实现Type接口中定义的方法(接收者为Value);

3)Value结构体对应定义方法的原型如下:

func (v Value) CanInterface() bool          // 是否可调用Interface()方法
func (v Value) Interface() (i interface{})  // 返回对象的反射值(可导出的)
func (v Value) IsNil() bool                 // 判断反射值是否为nil

func (v Value) Call(in []Value) []Value // 调用代表的函数/方法,参数需通过Value类型的Slice传入

func (v Value) Send(x Value)              // 向代表通道发送数据(阻塞)
func (v Value) TrySend(x Value) bool      // 尝试向代表通道发送数据x(无阻塞)
func (v Value) Recv() (x Value, ok bool)  // 从代表通道接收数组(阻塞)
func (v Value) TryRcv()(x Value, ok bool) // 尝试从代表通道接收数据(无阻塞)
func (v Value) Close()                    // 关闭代表通道

func (v Value) CanSet() bool   // 检测v是否可被修改
// 不可修改则返回false,且任何形式的Set方法调用都会panic

func (v Value) Set(x Value)                 // 将v的值改为其他Value类型的值为x
func (v Value) SetBool(x bool)              // 修改bool类型的值为x
func (v Value) SetInt(x int64)              // 修改整数类型的值为x
func (v Value) SetUint(x uint64)            // 修改无符号整数类型的值为x
func (v Value) SetFloat(x float64)          // 修改浮点数类型的值为x
func (v Value) SetComplex(x complex128)     // 修改复数类型的值为x
func (v Value) SetBytes(x []byte)           // 修改byte类型的值为x
func (v Value) SetString(x string)          // 修改字符串类型的值为x
func (v Value) SetPointer(x unsafe.Pointer) // 修改指针类型的值为x
func (v Value) SetCap(n int)                // 修改Slice的容量为n
func (v Value) SetLen(n int)                // 修改Slice的长度为n
func (v Value) SetMapIndex(key, val Value)  // 修改/添加map的键值对



常用函数定义原型如下:

1)将src中的元素复制到dst中, 返回复制成功个数(两者必须是数组或slice) 
func Copy(dst, src Value) int 

2)创建指向typ类型的指针(第二个参数传入地址)
func NewAt(typ Type, p unsfae.Pointer) Value

3)创建指定类型的对象,其值为零值
func Zero(typ Type) Value

4)创建指定长度和容量的Slice
func MakeSlice(typ Type, len, cap int) Value

5)创建键值类型均为typ的map
func MakeMap(typ Type) Value

6)创建函数
func MakeFUnc(typ Type, fn func(args []Value) (results []Value)) Value 

7)创建元素类型为typ,buff个缓冲的通道
func MakeChan(typ Type, buffer int) Value

8) 向s切片末尾添加多个元素
func Append(s Value, x ...Value) Value

9) 向s切片末尾添加t元素
func AppendSLice(s, t Value) Value

10)判断任意两个对象的底层是否完全相等(常用于比较结构体成员和空接口类型变量)
func DeepEqual(a1, a2 interface{}) bool


如:通过反射值修改对象的字段

1)编写程序;

package main

import (
    "fmt"
    "reflect"
)

type Animal struct {
    Name string
    Age  int
}

func (a Animal) Meth(name string) {
    fmt.Println("Name Is:", name)
}

func main() {
    cat := Animal{"mimi", 12}
    CatValue := reflect.ValueOf(cat)

    CatMeth := CatValue.MethodByName("Meth")
    args := []reflect.Value{reflect.ValueOf("maomao")}
    CatMeth.Call(args)

    dog := &Animal{"wangwang", 10}
    DogValue := reflect.ValueOf(dog)
    if DogValue.Kind() != reflect.Ptr {
        fmt.Println("必须为指针类型才可修改")
        return
    }

    name := DogValue.Elem().FieldByName("Name")
    if name.Kind() == reflect.String {
        name.SetString("gogo")
    }
    fmt.Println(*dog)
}

2)运行结果;
在这里插入图片描述
//被修改对象和调用的函数/方法必须是可导出的

posted @ 2022-05-07 08:51  爱和可乐的w  阅读(117)  评论(0编辑  收藏  举报