您的位置 首页 golang

大白话 golang 教程-13-断言转换和类型反射

前面已经接触过断言了,它的写法有两种形式:

 t := i.(T) // 1
t, ok := i.(T) // 2  

以上是希望把 i 转换成 T 类型的对象,第一种写法如果转换失败,会触发系统的 panic,导致程序退出;第二种写法如果断言失败,ok 被设置成 false,而 t 被设置成零值。

特殊情况是如果 i 是 nil 也会断言失败,哪怕是转成 interface{} 也不行。

 var i interface{} // 默认是 nil 值
_ = i.(interface{}) // panic 
_ = i.(string) // panic: interface conversion: interface {} is nil, not string  

如果能知道所有预知的类型就不需要断言了,处理起来也会更加高效、可靠,如何得到类型呢?

 func caseType(what interface{}) {
  switch what.(type) {
  case int:
    what = what.(int) + 1
    fmt.Printf("number %d\n", what)
  case string:
    fmt.Printf("string %s\n", what.(string)+" suffix")
  case nil:
    fmt.Println("you are nothing")
  }
}  

使用 i.(type) 获取了 i 的类型,这个语法结构叫 type-switch 断言,也就是 i.(type) 只能用在 switch 中,要直接获取类型也只能使用反射来实现,下面的代码实现和上面 type-switch 相同的功能。

 func detectType(what interface{}) {
  val := reflect.ValueOf(what)

  t := val.Type()       // Type 是接口对象
  fmt.Printf("%T\n", t) // *reflect.rtype

  //fmt.Println(t.Name()) // 类型名称

  if val.Kind() == reflect.Int {
    number := val.Int() + 1
    fmt.Printf("number %d\n", number) // number 11
  } else if val.Kind() == reflect.String {
    fmt.Printf("string %s\n", val.String()+" suffix") // string test suffix
  }
}  

reflect.ValueOf(what) 获取对象的运行时信息,val.Type 获取对象的类型描述信息(类型、字段、名称、大小、对齐等),它是一个 Type 接口对象 type Type interface {…},val.Kind() 获取反射的数据类型,它其实是 type Kind uint {…} 的定义,这些类型在 go 源文件 src/reflect/type.go 中定义,包括 Bool、Int、Uint、Float、Complex、Array、Chan、Func、Interface、Map、Ptr、Slice、String、Struct、UnsafePointer,通过对每一种类型的特殊处理,就能完全的动态的去处理一个对象。

什么是特殊处理,以指针对象为例,如果 what 是一个指针对象,那么 reflect.ValueOf(what) 的结果是 reflect.Ptr,要获取它指向的对象需要继续调用 val.Elem() 函数,可以提取出这样一个函数

 func getReflectValue(x interface{}) reflect.Value {
  val := reflect.ValueOf(x)
  // Uintptr
  // UnsafePointer
  if val.Kind() == reflect.Ptr {
    val = val.Elem()
  }
  return val
}  

对于 int、string、float 等简单的值可以直接获取,但是对于 reflect.Array 和 reflect.Slice 就需要遍历它的长度 val.Len(),使用 val.Index(index).Interface() 来获取它的值,至于它的元素是什么值呢? 对了,还是判断它的类型,所以要完成一个对象的反射遍历,必然是一个递归函数。

对 reflect.Struct 的处理也很特殊,它的字段数由 val.NumField() 函数获取,字段由 val.Field(index) 来获取,同样的对 reflect.Map 需要使用 val.MapKeys() 获取键集合,val.MapIndex(key) 来获取键对应的值。

总结一下大概需要分 5 种情况处理:

1. 数值型的 reflect.Int、reflect.Uint、reflect.Float、reflect.Complex

2. 布尔和字符串 reflect.Bool、reflect.String

3. 集合 reflect.Array、reflect.Slice

4. 键值对 reflect.Map

5. 特殊的 reflect.Ptr、refelect.Interface、reflect.Chan、reflect.Func

 func reflectIterate(x interface{}, handler func(val int)) {
  val := getReflectValue(x)

  switch val.Kind() {
  case reflect.Int, reflect.Uint, reflect.Float32, reflect.Complex64:
    // 其他不同位数的数值类型
    // 以简单值调用 handler
  case reflect.Bool:
    // 以简单值调用 handler
  case reflect.String:
    // 以简单值调用 handler
  case reflect.Interface:
    // 特殊的
  case reflect.Chan:
    // 特殊的
  case reflect.Func:
    // 特殊的
  case reflect.Struct:
    for i := 0; i < val.NumField(); i++ {
      reflectIterate(val.Field(i).Interface(), handler)
    }
  case reflect.Slice, reflect.Array:
    for i := 0; i < val.Len(); i++ {
      reflectIterate(val.Index(i).Interface(), handler)
    }
  case reflect.Map:
    for _, key := range val.MapKeys() {
      reflectIterate(val.MapIndex(key).Interface(), handler)
    }
  }
}  

本章节的代码

文章来源:智云一二三科技

文章标题:大白话 golang 教程-13-断言转换和类型反射

文章地址:https://www.zhihuclub.com/86935.shtml

关于作者: 智云科技

热门文章

网站地图