> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/golang/fan-she.md).

# 反射

golang的反射主要包含两个类型：`reflect.Type`和`reflect.Value`，他们主要由函数`reflect.TypeOf(变量名)`以及`reflect.ValueOf(变量名)`创建而来。其中`Type`用于表示目标变量的类型的元数据，而通过`Value`则是代表变量的值（可以对值进行修改、函数值进行函数调用等）。

1. 在运行时动态获取变量的各种信息，比如变量的类型(type)、类别(kind)
2. 如果是结构体变量，还可以获取结构体本身的 的信息(字段、方法)
3. 通过反射可以修改变量的值，也可以调用关联方法
4. 使用反射需要引入包`import "reflect"`

## 反射的基本函数

```go
package main

import (
	"fmt"
	"reflect"
)

func main() {

	var n int64 = 10

	// 获取变量类型信息 reflect.Type
	rTyp := reflect.TypeOf(n)
	fmt.Printf("rType: type=%T, toString=%s\n", rTyp, rTyp.String())

	// 获取变量值信息 reflect.Value
	rVal := reflect.ValueOf(n)
	fmt.Printf("rVal: type=%T, toString=%s, originValue=%d\n", rVal, rVal.String(), rVal.Int()) // 转换为原始类型，如果调用的方法与实际类型不匹配，会发生panic异常

	// 获取变量对应的kind，type代表变量的具体类型，而kind代表变量的分类类型
	// 如果是基本类型，kind==type，比如 kind=int64 type=int64
	// 如果不是基本类型，比如struct，type与kind一般不同，比如 kind=struct type=main.Student
	kind1 := rTyp.Kind()
	kind2 := rVal.Kind()
	fmt.Printf("kind=%s, %s", kind1, kind2)


	// 根据 rVal 获取变量值，因为不知道是什么类型，所以以interface的方式返回
	iV := rVal.Interface()
	fmt.Printf("iV: type=%T, originValue=%v\n ", iV, iV)

	// 将interface{} 通过断言转换成需要的类型
	n2 := iV.(int64)
	fmt.Println(n2)
}
```

返回结果：

```
rType: type=*reflect.rtype, toString=int64
rVal: type=reflect.Value, toString=<int64 Value>, originValue=10
kind=int64, int64iV: type=int64, originValue=10
10
```

## 通过反射修改值

在 Golang 中，通过 反射 的方法`reflect.ValueOf`传入的不管是变量的值还是地址，通过反射都不可以直接修改变量的值，如果尝试修改，将会引发一个`panic`：

```go
package main

import (
	"fmt"
	"reflect"
)

func main() {
	i := 10
	rVal := reflect.ValueOf(i) // panic: reflect: reflect.Value.SetInt using unaddressable value
	rVal.SetInt(11)
	fmt.Println(i)
}
```

上面的异常是说，`SetInt`方法，也就是修改变量值的反射方法，不可以用在一个不可取地址的`reflect.Value`上，通过`reflect.ValueOf`传入变量获取的`reflect.Value`都是不可取地址值。这是因为无论什么类型的变量，`relect.Value`都是存储变量的指针(地址)。

1. 值类型值传入

   ```go
   var x float64 = 3.4
   v := reflect.ValueOf(x) // x 值传入，ValueOf接收到的x已经不是原来的x
   v.SetFloat(7.1) // 这句话及时执行成功，也无法修改变量x的值
   ```
2. 值类型取地址传入

   ```go
   var x float64 = 3.4
   p := reflect.ValueOf(&x) // 注意：获取 x 的地址
   fmt.Println("type of p:", p.Type())
   fmt.Println("settability of p:", p.CanSet()) // CanSet用来判断是否是可取地址的， 此处返回false
   // 此处仍然是不可取地址的，因为p内部存储的是变量的地址，需要通过类似 *x 的方式真正访问变量空间
   ```
3. 使用`Elem()`方法访问变量内存

   ```go
   v := p.Elem()
   fmt.Println("settability of v:", v.CanSet()) // true
   v.SetFloat(7.1)
   fmt.Println(v.Interface()) // 7.1
   fmt.Println(x) // 7.1
   ```
4. 结构体值访问

   ```go
   type T struct {
       A int
       B string
   }
   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 = %v\n", i,
           typeOfT.Field(i).Name, f.Type(), f.Interface())
   }
   ```
5. 结构体字段值修改

   ```go
   s.Field(0).SetInt(77)
   s.Field(1).SetString("Sunset Strip")
   fmt.Println("t is now", t)
   ```

## 反射操作方法与函数

```go
// 获取add函数Type
vType:=reflect.TypeOf(add)

// 返回func类型的参数个数，如果不是函数，将会panic 
numIn:=vType.NumIn()

addIn:=make([]reflect.Type，numIn) 
for i:=0;i<numIn;i++{ addIn[i]=vType.In(i) 
```
