是什么

反射是程序在运行期间探知对象的类型信息和内存结构。如果不用反射,可以用汇编去操作。

为什么要用反射

使用反射的两个场景

  1. 有时你需要编写一个函数,但是并不知道传给你的参数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。
  2. 有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。

    不使用反射的理由

  3. 与反射相关的代码,经常是难以阅读的。在软件工程中,代码可读性也是一个非常重要的指标。

  4. Go 语言作为一门静态语言,编码过程中,编译器能提前发现一些类型错误,但是对于反射代码是无能为力的。所以包含反射相关的代码,很可能会运行很久,才会出错,这时候经常是直接 panic,可能会造成严重的后果。
  5. 反射对性能影响还是比较大的,比正常代码运行速度慢一到两个数量级。所以,对于一个项目中处于运行效率关键位置的代码,尽量避免使用反射特性。

反射如何实现的?

反射就是通过接口的类型信息实现的,反射建立在类型的基础上。

反射的基本函数

reflect包
reflect.Type主要提供关于类型相关的信息, reflect.Value结合_type 和data两者
reflect包中提供了两个基础的关于反射的函数来获取 reflect.Type和reflect.Value的函数

  1. func TypeOf (i interface{}) Type // TypeOf函数用来提取一个接口中值的类型信息
  2. func ValueOf(i interface{}) Value // reflect.Value表示 interface{} 里存储的实际变量,它能提供实际变量额各种信息

反射的三大法则

  1. 从 interface{}变量可以反射出反射对象。
  2. 从反射对象可以获取interface{}变量。
  3. 要修改反射对象,其值必须可设置。—直接传递地址,然后通过ValueOf.Elem()来进行操作 ```go Reflection goes from interface value to reflection object.

Reflection goes from reflection object to interface value.

To modify a reflection object, the value must be settable.

  1. <a name="dFNuT"></a>
  2. ### 反射相关函数的使用
  3. <a name="OlTJp"></a>
  4. #### 未导出成员
  5. 未导出成员特指 **结构体中首字母小写的私有变量 **,** 可以读取,但是不能通过反射修改其值。**<br />**导出成员特指结构体中字段名是首字母大写的,结构体中被修改的成员只能是导出成员**
  6. <a name="qxjAh"></a>
  7. ### 反射的实际应用
  8. <a name="G0Sh7"></a>
  9. #### 反射的应用: 对象序列化(json函数库)、 fmt相关函数、 ORM对象关系映射、
  10. <a name="FN4S1"></a>
  11. #### json序列化:
  12. ```go
  13. Marshal()
  14. Unmarshal()

DeepEqual的作用及原理

测试函数中,判断两个变量的实际内容完全一致
如何判断两个 slice 所有的元素完全相同;如何判断两个 map 的 key 和 value 完全相同等等。—都可以通过DeepEqual()函数实现。