1.方法和类型的反射

反射可以在运行时检查类型和变量,例如它的大小、方法和 动态 的调用这些方法。这对于没有源代码的包尤其有用。这是一个强大的工具,除非真得有必要,否则应当避免使用或小心使用。

reflect.TypeOf 和 reflect.ValueOf,返回被检查对象的类型和值
Interface() 方法可以得到还原(接口)值
Kind 总是返回底层类型

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. var x float64 = 3.4
  8. fmt.Println("type:", reflect.TypeOf(x))
  9. v := reflect.ValueOf(x)
  10. fmt.Println("value:", v)
  11. fmt.Println("type:", v.Type())
  12. fmt.Println("kind:", v.Kind())
  13. fmt.Println("value:", v.Float())
  14. fmt.Println(v.Interface())
  15. fmt.Printf("value is %5.2e\n", v.Interface())
  16. y := v.Interface().(float64)
  17. fmt.Println(y)
  18. }
  19. /**
  20. type: float64
  21. value: 3.4
  22. type: float64
  23. kind: float64
  24. value: 3.4
  25. 3.4
  26. value is 3.40e+00
  27. 3.4
  28. **/

2 通过反射修改(设置)值

可以使用 CanSet() 方法测试是否可设置
要想让其可设置我们需要使用 Elem() 函数,这间接的使用指针:v = v.Elem()

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. var x float64 = 3.14
  8. v := reflect.ValueOf(x)
  9. //setting a value
  10. //v.SetFloat(3.1415) // Error: will panic: reflect.Value.SetFloat using unaddressable value
  11. fmt.Println("settability of v:", v.CanSet())
  12. v = reflect.ValueOf(&x)
  13. fmt.Println("type of v", v.Type())
  14. fmt.Println("settability of v:", v.CanSet())
  15. v = v.Elem()
  16. fmt.Println("the elem of v is:", v)
  17. fmt.Println("settability of v:", v.CanSet())
  18. v.SetFloat(3.1415)
  19. fmt.Println(v.Interface())
  20. fmt.Println(v)
  21. }
  22. /**
  23. settability of v: false
  24. type of v *float64
  25. settability of v: false
  26. the elem of v is: 3.14
  27. settability of v: true
  28. 3.1415
  29. 3.1415
  30. **/

3.反射结构

有些时候需要反射一个结构类型。NumField() 方法返回结构内的字段数量;通过一个 for 循环用索引取得每个字段的值 Field(i)。
我们同样能够调用签名在结构上的方法,例如,使用索引 n 来调用:Method(n).Call(nil)。

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type NotknownType struct {
  7. s1, s2, s3 string
  8. }
  9. func (n NotknownType) String() string {
  10. return n.s1 + " - " + n.s2 + " - " + n.s3
  11. }
  12. var secret interface{} = NotknownType{"ada", "go", "object"}
  13. func main() {
  14. value := reflect.ValueOf(secret)
  15. typ := reflect.TypeOf(secret)
  16. //alternative
  17. //typ := value.Type()
  18. fmt.Println(value)
  19. fmt.Println(typ)
  20. knd := value.Kind()
  21. fmt.Println(knd)
  22. //iterate through the fields of struct
  23. for i := 0; i < value.NumField(); i++ {
  24. fmt.Printf("Field %d: %v\n", i, value.Field(i))
  25. //value.Field(i).SetString("C#") //error: panic: reflect.Value.SetString using value obtained using unexported field
  26. }
  27. //call the first method, which is String():
  28. results := value.Method(0).Call(nil)
  29. fmt.Println(results)
  30. }

但是如果尝试更改一个值,会得到一个错误,因为结构中只有被导出字段(首字母大写)才是可设置的

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. type T struct {
  7. A int
  8. B string
  9. }
  10. func main() {
  11. t := T{23, "skidoo"}
  12. s := reflect.ValueOf(&t).Elem()
  13. typeOfT := s.Type()
  14. for i := 0; i < s.NumField(); i++ {
  15. f := s.Field(i)
  16. fmt.Printf("%d: %s %s = %v\n", i,
  17. typeOfT.Field(i).Name, f.Type(), f.Interface())
  18. }
  19. s.Field(0).SetInt(77)
  20. s.Field(1).SetString("test")
  21. fmt.Println("t is now", t)
  22. }
  23. /**
  24. 0: A int = 23
  25. 1: B string = skidoo
  26. t is now {77 test}
  27. **/

4.printf和反射

fmt 包中的 Printf(以及其他格式化输出函数)都会使用反射来分析它的 … 参数。

  1. package main
  2. import (
  3. "os"
  4. "strconv"
  5. )
  6. type Stringer interface {
  7. String() string
  8. }
  9. type Celsius float64
  10. func (c Celsius) String() string {
  11. return strconv.FormatFloat(float64(c), 'f', 1, 64) + " ℃"
  12. }
  13. type Day int
  14. var dayName = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
  15. func (day Day) String() string {
  16. return dayName[day]
  17. }
  18. func print(args ...interface{}) {
  19. for i, args := range args {
  20. if i > 0 {
  21. os.Stdout.WriteString(" ")
  22. }
  23. switch a := args.(type) {
  24. case Stringer:
  25. os.Stdout.WriteString(a.String())
  26. case int:
  27. os.Stdout.WriteString(strconv.Itoa(a))
  28. case string:
  29. os.Stdout.WriteString(a)
  30. default:
  31. os.Stdout.WriteString("???")
  32. }
  33. }
  34. }
  35. func main() {
  36. print(Day(1), "was", Celsius(18.36))
  37. }