Golang的Reflect包是做什么用的?

Reflection in computing is the ability of a program to examine its own structure, particularly through types;it’s a form of metaprogramming
反射是程序检查其自身结构的能力,尤其是通过类型;它是元编程的一种形式。
From The Go Blog - The Laws of Reflection

上面是反射的定义,也就是golang Reflect包设计的目的。

元编程是什么?

元编程是一种编程技术,其中计算机程序具有将其他程序视为其数据的能力。这意味着可以设计一个程序来读取,生成,分析或转换其他程序,甚至在运行时对其进行修改。在某些情况下,这使程序员可以最大程度地减少表达解决方案的代码行数,从而减少开发时间。它还使程序具有更大的灵活性,可以有效地处理新情况而无需重新编译
From: wiki - Metaprogramming

interface

在Golng中,interface和reflection是互相依赖的特性,了解什么事反射前,需要了解好interface。

io.Readerio.Wirter
// Reader is the interface that wraps the basic Read method.
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Writer is the interface that wraps the basic Write method.
type Writer interface {
    Write(p []byte) (n int, err error)
}
ReadReadfunc Printer(r io.Reader) {}
interface{}

现在就可以一起看看golang中的反射机制了

Reflect.TypeOf和Reflect.ValueOf

key-valuereflect.Typereflet.Value

下面针对golang已有的变量类型去看看如何通过reflect去操作变量的例子。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x)) 
    // =================
    // type: float64
   
    var x float64 = 3.4
	fmt.Println("value:", reflect.ValueOf(x).String())
	// =================
	// value: <float64 Value>
}
reflect.Typereflect.Value
var x uint8 = 'x'
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())                            // uint8.
fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
x = uint8(v.Uint())                                       // v.Uint returns a uint64.
reflect.ValueInterface()refect.Valueinterface
y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)

使用反射操作变量

reflect.ValueCanSet()
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
// ==============
// settability of v: false
变量v变量x变量x

而正确的方式应该是,先取变量地址的值,然后通过指针获取变量的值

var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: 取x的地址.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())
// ================
// type of p: *float64
// settability of p: false

v := p.Elem() // Note: 取地址指向的具体值
fmt.Println("settability of v:", v.CanSet())
// ==============
settability of v: true

使用reflect操作struct

上面说了这么多,现在我们再回到主题,这才是这边文章想要说的东西。

interface{}struct

上面的简单例子,是用反射修改变量的值,如果变量类型是struct,其实同样可以也是修改struct field的值的

reflect.Value
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())
}

// ===================
// 0: A int = 23
// 1: B string = skidoo
TagsTags

而修改struct字段,可以

s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)
// ===============
// t is now {77 Sunset Strip}