go语言学习之reflect

2023-01-04 14:07:34 浏览数 (1)

本学习笔记全部以代码块的形式展示,具体的内容都包含在代码里:

代码语言:javascript复制
package types

import (
  "fmt"
  "reflect"
)

// 1. reflect 是用程序检查其所拥有的结构,尤其是类型的一种能力;这是元编程的一种形式。
//     反射可以在运行时检查类型和变量,例如它的大小、方法,并且能动态的调用其中的方法。
//  2. reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值。
// 3. 原理上,反射是通过检查一个接口的值,变量首先被转换成空接口。
// 4. 反射可以从接口值反射到对象,也可以从对象反射回接口值。
// 5. reflect.Type 和 reflect.Value 都有许多方法用于检查和操作自身
// 6. 利用reflect 可以更改对象值,反射中有些内容是需要用地址去改变它的状态的。
// 7. reflect 可以反射结构struct。

// 定义 NotKnownType
type NotKnownType struct {
  a, b, c string
}

// 定义方法
func (n NotKnownType) GetStr() string {
  return n.a   n.b   n.c
}
func (n NotKnownType) GetStr1(str string) string {
  return n.a   n.b   n.c   str
}

// 定义 NotKnownType
type NotKnownType1 struct {
  A, B, C string
}

func ReflectExample() {
  var x int8 = 10
  // t 是 reflect.Type 类型
  t := reflect.TypeOf(x)
  // v 是 reflect.Value 类型
  v := reflect.ValueOf(x)

  fmt.Println("t: ", t)
  fmt.Println("v: ", v)

  // 5
  fmt.Println("reflect.Value.Type: ", v.Type())
  // Kind 方法总是返回底层类型
  fmt.Println("reflect.Type.Kind, reflect.Value.Kind: ", t.Kind(), v.Kind(), t.Kind() == reflect.Int8)
  fmt.Println("reflect.Value.Int: ", v.Int())

  // 6
  // 使用 CanSet 判断是否可以更改值
  fmt.Println("reflect.Value.CanSet: ", v.CanSet())
  // 上述得到的是 false,因为 v 是通过 x 的拷贝创建的,改变 v 并不能改变 x,所以要使用 x的地址
  v1 := reflect.ValueOf(&x)
  fmt.Println("v1: ", v1, v1.Type())
  // 这时还是不能更改值
  fmt.Println("reflect.Value.CanSet: ", v1.CanSet())
  // 还需要使用 Elem()
  v1 = v1.Elem()
  fmt.Println("reflect.Value.CanSet: ", v1.CanSet())
  // 更改值
  v1.SetInt(20)
  fmt.Println("reflect.Value.SetInt: ", v1, x)

  // 7
  // 假设不知道 n 具体的struct类型
  var n interface{} = NotKnownType{"a", "b", "c"}
  t2 := reflect.TypeOf(n)
  v2 := reflect.ValueOf(n)

  fmt.Println("t2: ", t2)
  fmt.Println("v3: ", v2)

  // 可使用 NumField 返回结构体的字段数量,使用 Field 得到该字段的值
  for i := 0; i < v2.NumField(); i   {
    fmt.Printf("reflect.Value.Field %d: %vn", i, v2.Field(i))
  }
  // 使用 Method(n).Call(nil) 调用结构体中的方法
  m1 := v2.Method(0).Call(nil)
  param := []reflect.Value{reflect.ValueOf("d")}
  m2 := v2.Method(1).Call(param)
  fmt.Printf("m1: %v, m2: %vn", m1, m2)

  // 当更改struct字段时,字段必须可导出(即首字母要大写),然后结合上述更改的规则
  n1 := NotKnownType1{"A", "B", "C"}
  v3 := reflect.ValueOf(&n1)
  v3 = v3.Elem()
  fmt.Println("reflect.Value.CanSet: ", v3.CanSet())
  for i := 0; i < v3.NumField(); i   {
    origin := v3.Field(i)
    v3.Field(i).SetString(origin.String()   origin.String())
  }
  fmt.Println("n1: ", n1)
}

0 人点赞