GoLang(7)反射

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tangyaya8/article/details/88074254

什么是反射

反射是指在程序运行的时候,可以修改它本身状态的一种行为和能力,它能获取到一些元数据,比如java语言中类的成员变量,方法,注解等元数据,go语言中结构体的field,方法等等,有了反射可以让语言变得更加灵活和更多的功能,比如java中的jdk自带的动态代理就用到了反射,再比如很多框架和封装的ORM也大量用到了反射机制,我们来了解一下go语言的反射。

GO语言中的反射

go是一个比较年轻的语言,吸收了很多语言的优点,同时也弥补了他们的不足。go是一个静态的语言,也带有反射机制,反射要获取的元数据,那整理下go语言的结构

  • 结构体(struct,interface)
  • 结构体里面的属性(field)
  • 方法(method)
    基于以上go语言提供的元数据,我们来学习go语言提供的反射机制

go语言将反射的元数据分为两个结构体来描述

类型(TypeOf)

可以获取要目标结构体/属性/方法所属对象的类型
新建结构体:

type Cat struct {
	Sex   string
	Color string
	Age   int
}
//为结构体提供两个方法
//吃的方法,没有入参
func (cat Cat) Eat() int {
	fmt.Println("fhaskfd")
	return 2
}
//唱歌的方法,具有两个入参
func (cat Cat) Sing(sing string,i int) int {
	fmt.Println(sing)
	fmt.Println(i,"66666")
	return 2
}
//测试TypeOf提供的方法:
func test1(){
	//新建对象
	cat := Cat{Sex:"male",Color:"red",Age:18}
	typeOf := reflect.TypeOf(cat)
	fmt.Println(typeOf)//输出:main.Cat -->包名.结构体名称
}

Kind()

go语言给go中的类型提供了一下枚举:

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
)

kind()返回目标type的具体类型

typeOf := reflect.TypeOf(cat)
kind := typeOf.Kind()
//输入Cat反射之后是一个结构体类型
fmt.Println(kind)//输出:struct
//新建一个string类型的来获取Kind
s := "Hello"
fmt.Println(reflect.TypeOf(s).Kind())//输出:string

Name()

Name在其包中返回已定义类型的类型名称

//类型的名称
name := typeOf.Name()
fmt.Println(name)//输出:Cat
fmt.Println(reflect.TypeOf(s).Name())//输出:string

PkgPath()

//导入的包的路径
pkgPath := typeOf.PkgPath()
fmt.Println(pkgPath)//输出main
fmt.Println(reflect.TypeOf(s).PkgPath()//输出“”空字符串

NumField()

返回结构体的属性的数量,如果不是结构体类型则panic

numField := typeOf.NumField()
fmt.Println(numField)//输出:3

Field(i int)

返回结构体中第i个属性的StructField
StructField是这样定义的:

type StructField struct {
	// Name is the field name.
	Name string
	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

我们用一个例子来看看:

for i := 0; i < typeOf.NumField(); i++ {
	fmt.Println(typeOf.Field(i))
}
//输出:
{Sex  string  0 [0] false}
{Color  string  16 [1] false}
{Age  int  32 [2] false}
//在Name和Type中间的PkgPath()其实是有值的,不过是“”空字符串,
//由于tag没有,所以这里也是空字符串

FieldByIndex(index []int) StructField

FieldByIndex返回对应的嵌套字段,链式调用,如果非结构体则panic

//新建结构体,里面只有一个匿名属性Cat
	Animal{
		Cat
	}
	i := []int{0,1}//访问结构体Animal里面的index为0的属性-->Cat---->访问Cat里面的第一个属性---Color
	fmt.Println(reflect.TypeOf(Animal{Cat{"mel", "red", 222,}}).FieldByIndex(i))
	//输出:	{Color  string  16 [1] false}

FieldByName(name string) (StructField, bool)

根据属性名称来获取Field,如果找到该属性,返回structField,并返回true,如果找不到返回nil,false

	field, b := typeOf.FieldByName("Sex")
	fmt.Println(field, b)//输出:{Sex  string  0 [0] false} true
	field, b := typeOf.FieldByName("Sex")
	fmt.Println(field, b)//输出:{  <nil>  0 [] false} false

NumMethod() int

结构体所具有的方法个数

//获取结构体所具有的方法
	numMethod := typeOf.NumMethod()
	fmt.Println(numMethod)//输出:2

Method(int) Method

// Method represents a single method.
type Method struct {
	// Name is the method name.
	// PkgPath is the package path that qualifies a lower case (unexported)
	// method name. It is empty for upper case (exported) method names.
	// The combination of PkgPath and Name uniquely identifies a method
	// in a method set.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	Name    string
	PkgPath string

	Type  Type  // method type
	Func  Value // func with receiver as first argument
	Index int   // index for Type.Method
}

返回结构体方法的第i个方法的结构

method := typeOf.Method(0)
	fmt.Println(method)//输出:{Eat  func(main.Cat) int <func(main.Cat) int Value> 0}

值(TypeValue)

可以获取目标对象的值
新建结构体Cat

type Cat struct {
	Sex   string
	Color string
	Age   int
}

获取Value:

valueOf := reflect.ValueOf(Cat{"mel", "red", 222,})
fmt.Println(valueOf)//直接输出结构体的值:{man blue 3}

Kind() Kind

返回Value的Kind类型

kind := valueOf.Kind()
fmt.Println(kind)

大集合

由于方法比较多,和TypeOf有些相似,大致说一说他们的用途

//获取结构体方法的个数
	fmt.Println(valueOf.NumMethod())
	//获取结构体属性的个数
	fmt.Println(valueOf.NumField())
	//根据索引获取结构体方法的值
	fmt.Println(valueOf.Method(0))
	//根据索引获取结构体的属性的值
	fmt.Println(valueOf.Field(0).Interface())

用反射修改属性的值

func main(){
	test3(&Cat{"man", "black", 11,})
}
func test3(o interface{}) {
	valueOf := reflect.ValueOf(o)
	fmt.Println(valueOf.Kind())
	if valueOf.Kind() == reflect.Ptr && !valueOf.Elem().CanSet() {
		fmt.Println("修改失败")
		return
	}
	//获取原始对象的引用并创建新对象
	elem := valueOf.Elem()
	name := elem.FieldByName("Sex")
	if !name.IsValid() {
		fmt.Println("修改失败")
		return
	}
	//设置值
	if name.Kind() == reflect.String {
		name.SetString("women")
	}
	fmt.Println(valueOf)

}

用反射调用方法并传参接收值

func main(){
	ReflectMethod(&Cat{"man", "black", 11,})
}
func ReflectMethod(o interface{}) {
	v := reflect.ValueOf(o)
	//无参函数调用
	m1 := v.MethodByName("Eat")
	//调用无参方法
	call := m1.Call([]reflect.Value{})
	for key, value := range call {
		fmt.Println(key, value)
	}
	//有参函数调用
	m2 := v.MethodByName("Sing")
	values := m2.Call([]reflect.Value{reflect.ValueOf("iris"), reflect.ValueOf(3)})
	for _, value := range values {
		fmt.Println(value)
	}
}

总结:

go语言为我们提供了遍历的反射机制,让我们能在程序运行的过程中修改元数据的值(可以修改的),利用反射并结合设计模式能设计出良好的程序,化繁为简。加油加油啊

猜你喜欢

转载自blog.csdn.net/tangyaya8/article/details/88074254
今日推荐