12_Go语言 反射详解 - Go语言中文社区

12_Go语言 反射详解


1. 反射定义:

可以在运行时动态获取变量的相关信息。 Import (“reflect”)

官方对此有个非常简明的介绍,两句话耐人寻味:

  1. 反射提供一种让程序检查自身结构的能力。再精确点的描述是“反射是一种检查interface变量的底层类型和值的机制”;
  2. 反射是困惑的源泉;(想深入了解反射,必须深入理解类型和接口概念。)

2. 方法:

  1. reflect.TypeOf,获取变量的类型,返回reflect.Type类型 ;
  2. reflect.ValueOf,获取变量的值,返回reflect.Value类型;
  3. reflect.Value.Kind,获取变量的类别,返回一个常量;
  4. reflect.Value.Interface(),转换成interface{}类型;
  5. reflect.ValueOf(x).Float(),获取变量的值;(reflect.ValueOf(x).Int()reflect.ValueOf(x).String()reflect.ValueOf(x).Bool()
  6. reflect.Value.SetXX相关方法,通过反射的来改变变量的值
    ,比如:reflect.Value.SetFloat(),设置浮点数;reflect.Value.SetInt(),设置整数;reflect.Value.SetString(),设置字符串

举个例子:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var x float64 = 3.4
	fmt.Println("type:", reflect.TypeOf(x))
	v := reflect.ValueOf(x)
	fmt.Println("value:", v)
	fmt.Println("type:", v.Type())
	fmt.Println("kind:", v.Kind())
	fmt.Println("value:", v.Float())

	fmt.Println(v.Interface())
	fmt.Printf("value is %5.2en", v.Interface())
	y := v.Interface().(float64)
	fmt.Println(y)
}

执行结果如下图:
在这里插入图片描述
再比如:

package main
import (
	"fmt"
	"reflect"
)
func main() {
	var a float64
	fv := reflect.ValueOf(&a)
	//其中fv.Elem()用来获取指针指向的变量,相当于: var a *int; *a = 100
	fv.Elem().SetFloat(3.3)
	fmt.Printf("%vn", a)
}

执行结果如下图所示:
在这里插入图片描述

3. 用反射操作结构体

  1. reflect.Value.NumField() 获取结构体中字段的个数
  2. reflect.Value.Method(n).Call 来调用结构体中的方法

举个例子(通过反射操作结构体):

package main

import (
	"fmt"
	"reflect"
)

type NotknownType struct {
	s1 string
	s2 string
	s3 string
}
func (n NotknownType) String() string {
	return n.s1 + "-" + n.s2 + "-" + n.s3
}
var secret interface{} = NotknownType{"Ada", "Go", "Oberon"}

func main() {
	value := reflect.ValueOf(secret) // <main.NotknownType Value>
	typ := reflect.TypeOf(secret)    // main.NotknownType
	fmt.Println(typ)

	knd := value.Kind() // struct
	fmt.Println(knd)

	for i := 0; i < value.NumField(); i++ {
		fmt.Printf("Field %d: %vn", i, value.Field(i))
		//value.Field(i).SetString("C#")
	}

	results := value.Method(0).Call(nil)
	fmt.Println(results) // [Ada - Go - Oberon]
}

执行结果如下图:
在这里插入图片描述
通过反射修改结构体:

package main

import (
	"fmt"
	"reflect"
)

type T struct {
	A int
	B string
}

func main() {
	t := T{23, "skidoo"}
	s := reflect.ValueOf(&t).Elem()
	typeOfT := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		fmt.Printf("%d: %s %s = %vn", i,
			typeOfT.Field(i).Name, f.Type(), f.Interface())
	}
	s.Field(0).SetInt(77)
	s.Field(1).SetString("Sunset Strip")
	fmt.Println("t is now", t)
}

执行结果如下图所示:
在这里插入图片描述

4. 反射三定律:

interface类型有个(value,type)对,而反射就是检查interface的这个(value, type)对的。具体一点说就是Go提供一组方法提取interface的value,提供另一组方法提取interface的type.

反射包里有两个接口类型要先了解一下.

  1. reflect.Type 提供一组接口处理interface的类型,即(value, type)中的type
  2. reflect.Value提供一组接口处理interface的值,即(value, type)中的value

反射第一定律:反射可以将interface类型变量转换成反射对象

package main

import (
	"fmt"
	"reflect"
)

func main() {
	//通过反射获取一个变量的值和类型的
	var x float64 = 3.4
	t := reflect.TypeOf(x)  //t is reflext.Type
	fmt.Println("type:", t)

	v := reflect.ValueOf(x) //v is reflext.Value
	fmt.Println("value:", v)
}

执行结果如下:
在这里插入图片描述
注意:反射是针对interface类型变量的,其中TypeOf()和ValueOf()接受的参数都是interface{}类型的,也即x值是被转成了interface传入的。

反射第二定律:反射可以将反射对象还原成interface对象

package main

import (
	"fmt"
	"reflect"
)
//之所以叫'反射',反射对象与interface对象是可以互相转化的
func main() {
	var x float64 = 3.4

	v := reflect.ValueOf(x) //v is reflext.Value

	var y float64 = v.Interface().(float64)
	fmt.Println("value:", y)
}

执行结果如下:
在这里插入图片描述
对象x转换成反射对象v,v又通过Interface()接口转换成interface对象,interface对象通过.(float64)类型断言获取float64类型的值。

反射第三定律:反射对象可修改,value值必须是可设置的

通过反射可以将interface类型变量转换成反射对象,可以使用该反射对象设置其持有的值。
reflect.Value提供了Elem()方法,可以获得指针向指向的value。看如下代码:

package main

import (
"reflect"
    "fmt"
)

func main() {
    var x float64 = 3.4
    v := reflect.ValueOf(&x)
    v.Elem().SetFloat(7.1)
    fmt.Println("x :", v.Elem().Interface())
}

执行结果如下图:
在这里插入图片描述
对反射的深入理解,个人觉得还需要继续看的内容:

  1. 参考业界,尤其是开源框架中是如何使用反射的
  2. 研究反射实现原理,探究其性能优化的手段

上一篇:06_Go语言进阶学习_ 接口详解

下一篇:08_Go语言进阶学习_ 排序、链表、二叉树

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/cui_yonghua/article/details/95058368
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-02-13 15:07:49
  • 阅读 ( 1299 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢