golang中反射知识点浅析

前端之家收集整理的这篇文章主要介绍了golang中反射知识点浅析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.reflect简介

反射,就是在程序运行过程中,能够获取任意一个类的属性方法;对任意一个类,能够调用他的任意方法属性。这是一个比较官方的解说。个人觉得通俗易懂的解释是:“通过一个给定的变量对象,获取这个变量的类型,这个类型的属性方法

2.重要接口与类

golang中reflect包

package reflect

重要接口

type Type interface{}

重要类

type Value struct{}

关键点
反射类也是有类型的,golang中的其他类型,怎么和反射搭上关系呢?

与Type接口类型变量勾搭上的方法是:

func TypeOf(i interface{}) Type

与Value类类型勾搭上的方法

func ValueOf(i interface{}) Value

上边两个函数,均是reflect包中的导出方法。可以在程序中使用,在与反射勾搭上之前,你只需要有一个变量对象,将这个变量,传入上边任何一个中,就可以与golang的反射勾搭上。引用不同的函数,返回值不一样,顾名思义,一个偏向于类型,一个偏向于值,通过ValueOf函数与反射勾搭上,具有更丰富的功能
example:

func main() {
    str := []string{"go","lang","reflect","example"}
    val := reflect.ValueOf(str)
    fmt.Println(val)
}

如上图所示,str是一个变量对象,通过reflect.ValueOf函数,将str变量对象与反射成功的勾搭上。

3. 反射能做什么

3.1 获取变量对象类型

reflect包中Value类提供了Kind方法,可以获取变量对象的类型

func main() {
    str := []string{"go","example"}
    val := reflect.ValueOf(str)
    vtype := val.Kind()
    fmt.Println(vtype)
}
//Output: slice

上边的例子是不是很傻?自己定义的str怎么可能不知道自己的类型呢?还假装自己不知道,用听起来高大上的反射做这么愚蠢的事情。下边再来一个例子:

func WhatIsYourType(i interface{}) string {
    val := reflect.ValueOf(i)
    return val.Kind().String()
}

func main() {
    str := []string{"go","example"}
    it := 2009
    sit := map[string]string{"hi": "girl","hello": "sir"}
    fmt.Println(WhatIsYourType(str))
    fmt.Println(WhatIsYourType(it))
    fmt.Println(WhatIsYourType(sit))
}
//Output:
// slice
// int
// map

假如有这么一个需求,一个函数,需要接收所有类型的变量,根据接收变量的类型,完成不同的操作。golang是一门强类型语言,自然这个函数如果想接收所有的类型,最好的办法就是申明这个参数类型为interface{},当这个函数接收到interface{}类型时,如果想知道传进来的那个变量真实类型,最好的办法是,通过反射来解决这个需求了。

上边通过Value提供的Kind方法获取了被反射操作对象的类型。Value提供的方法有很多,几乎大多数方法都有他使用的范围,但是,对于Kind()方法,reflect包中定义的所有反射类型,都可以使用这个方法

3.2 获取变量对象属性

我们可以通过reflect获取类型为slice的对象变量的属性吗?答案是:不行。

虽然,我们已经通过ValueOf,将slice类型的对象变量与reflect勾搭上了,但是Value提供的一系列方法中,大多数方法,只能针对特定的类型使用,如获取对象属性方法,前提是这个对象要有属性能够被获取,golang中哪些类型会包含属性了,struct就是其中一个。所以,通过反射获取变量对象属性,是针对于对象变量是struct设计的,如果乱用,在slice,Array中使用获取属性的反射方法,就会panic。反射,是一个比较方便的编程手段,用的好,可以帮助我们减少很多代码量,大大提高API的处理范围;但是用的不好,就会深藏很多panic,如果没有捕获异常,就会导致系统奔溃。

下边来看一个获取对象属性的例子:

type Ref struct {
    key string
    val string
}
func main() {
    ref := Ref{key: "hi",val: "girl"}
    iref := reflect.ValueOf(ref)
    fmt.Println(iref.Field(0).String())
    fmt.Println(iref.Field(1).String())
}
// Output:
// hi
// girl

通过反射,我们获取到了struct类型对象变量的值,即使这个值是私有的,照样可以获取

通过上边的例子,使用反射,获取到了对象变量的属性值。假如在初始化Ref结构体时,使用的是指针呢?获取对象变量的属性还会是这样写吗?答案是:不是。

在reflect包中,Value类提供了很多方法,但是几乎各个方法,都有其适用的对象类型。也就是说,被反射的对象变量是什么类型,就决定过了,就决定了,Value提供的哪些方法可用,哪些方法不可用。如上边获取属性的Field()方法,就只能当被反射的对象是struct类型时,才能调用。如果初始化struct为指针,则需要首先取出指针指向地方的内容,如果指向的struct类型,则才能调用Field()方法。reflect包中,获取指针指向内容方法时Elem,也可以用Indirect()方法
下边是反射对象是struct指针时,获取属性代码示例:

ref := &Ref{key: "hi",val: "girl"}
    iref := reflect.ValueOf(ref)
    fmt.Println(iref.Elem().Field(0).String())
    fmt.Println(iref.Elem().Field(1).String())

注意:Elem方法,也有其适用范围,就是被反射的对象必须是Ptr或者Interface类型。

3.3 获取变量对象方法

想要获取变量对象的方法,前提是,这个被反射的变量对象能够有自己的方法,golang中,处处都是类,所以,除了Interface只让声明,不让实现自己的方法外,其余的类型,基本上都可以有自己的方法。所以,获取变量对象方法方法,适用于大多数变量对象。
下边来一个例子,来实操reflect操作变量对象的方法

type Ref struct {
    key string
    val string
}

func (this *Ref) SayHi(str interface{}) {
    fmt.Println("reflect say:",str)
}

func main() {
    ref := &Ref{key: "hi",val: "girl"}
    iref := reflect.ValueOf(ref)
    fn := iref.MethodByName("SayHi")
    var str = "hello reflect"
    var in []reflect.Value
    in = append(in,reflect.ValueOf(str))
    fn.Call(in)
}

上边例子,实现了reflect调用变量对象的方法。通过上边3个例子,实现了反射访问变量对象类型,属性方法

其实反射的作用远远不止上边几个,在使用reflect的过程中,你会发现更多有趣的玩法。

总结:
弄清楚reflect包中Value提供的每一种方法使用的范围,否则你会遇到很多panic

猜你在找的Go相关文章