[转载] golang 反射

  • A+
所属分类:golang


曾阅读[今日头条中关于 Golang 优化](今日头条Go建千亿级微服务的实践)的一篇文章,说尽可能不要用反射

go 中 interface 实现了 duck-typing,那么一个问题就是通过 `interface` 变量来重新寻找原始的值

  • 编译时:静态
  • 运行时:动态

[Data structure: interface](Go Data Structures: Interfaces)

介绍了 interface 变量中既存储了相应的 `type` 和 `value`

反射三大法则:

  • Reflection goes from interface value to reflection object.
  • Reflection goes from reflection object to interface value.
  • To modify the object, the value must be settable.
var i Interface
i = obj

上述发生的是 copy 复制,也就是说 interface 变量底层真实存储的是一个跟 `obj` 完全不同的变量,所以 interface 中的 `data` 与 `obj` 是独立变化的

var x float64 = 3.4
fmt.Println(reflect.TypeOf(x))      // 获取 x 类型
fmt.Println(reflect.ValueOf(x))     // 获取 x 值
fmt.Println(reflect.ValueOf(x).Type())  // 我们可以从类型中获得值
fmt.Println(reflect.ValueOf(x).Kind))  // 获取存储对应的基本类型,如 reflect.Float64 reflect.Slice reflect.Struct
var x float64 = 3.4
v := reflect.ValueOf(x)
switch v.Kind() {
    case reflect.Int:
        fmt.Println(v.Int(()))
    case reflect.Float64:
        fmt.Println(v.Float())
}

对应 reflect 中对于类型的操作 `setter getter`,总是采用最大的类型,比如 uint64 int64 float64 等

var x uint8 = 'x'
v := reflct.ValueOf(x)
fmt.Printf("%T", v.Uint())  // uint64
x = uint8(v.Uint())         // 需要把类型转换到需要的类型

`Type()` 显示的是静态类型

`Kind()` 显示的是底层的类型,而不是静态类型

type MyInt int
x := MyInt(100)
v := reflect.ValueOf(x)
fmt.Println(x.Kind())   // int,而不是 MyInt
fmt.Println(x.Type())   // main.MyInt,而不是 in

`interface <==> reflection` 这两者之间是可以转化的

var x float64 = 1.0
v := reflect.ValueOf(x)
y := v.Interface()
fmt.Println(y)      // 1.0
// 提取处 float64
f := y.(float64)

不是所有 reflect.Value 都可以修改的

var x float64 = 1.0
v := reflect.ValueOf(x)
v.SetFloat(2.5)
panic: reflect: reflect.Value.SetFloat using unaddressable value

会被抛出,原因不是 2.5 不能够被寻址,而是 v 不是可以修改的

通过 `CanSet` 方法查看某个 reflection object 是否可以修改

var x float64 = 1.0
v := reflect.ValueOf(x)
fmt.Println(v.CanSet())     // false

在函数传递参数时,go 是进行值传递,完全 coppy 了一个新的值,所以即使我们在方法中修改了值,外围调用函数也不会收到影响。

所以我们 `reflect.ValueOf()` 传递的是 copy,也就不能够 set。

所以应该传递指针

var x float64 = 1.0
v := reflect.ValueOf(&x)
fmt.Println(v.CanSet())     // false
p := v.Elem()
fmt.Println(p.CanSet())     // true
p.SetFloat(2.5)
fmt.Println(x)              // 2.5
fmt.Println(*(v.Interface().(*float64)))    // 2.5

通过反射修改 struct 的值

type Human struct {
	name string
	age  int
}

h := Human{"hello", 100}
v := reflect.ValueOf(&h).Elem()
typeOf := v.Type()
for i := 0; i < v.NumField(); i++ {
	f := v.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i, typeOf.Field(i).Name, f.Type(), f)
}

output:

0: name string = hello
1: age int = 100

struct 中只有大写开头的属性才能够被修改:

type Human struct {
    Name string
    age int
}

h := Human{"hello", 100}
v := reflect.ValueOf(&h).Elem()
v.Field(0).SetString("world")
typeOf := v.Type()
for i := 0; i < v.NumField(); i++ {
	f := v.Field(i)
	fmt.Printf("%d: %s %s = %v\n", i, typeOf.Field(i).Name, f.Type(), f)
}
fmt.Println(h)

output:

0: Name string = world
1: age int = 100
{world 100}

如果 Human 中的名字是小写 name,那么就会引发错误:`panic: reflect: reflect.Value.SetString using value obtained using unexported field`

weinxin
我的微信
欢迎来撩!!

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: