首先给大家推荐一个在线 Golang 运行环境,可以测试剪短的代码逻辑。https://play.studygolang.com

Golang 中的反射是基于类型(type)机制的,所以需要重温一下 Golang 中的类型机制。

一、Types and interfaces

int,float32,*MyType,[]byte
type MyInt int

var i int
var j MyInt

则变量 i 是 int 类型,变量 j 是 MyInt 类型。变量 i 和 j 具有不同的静态类型,尽管它们具有相同的基础类型,但是如果不进行转换依然无法将其中一个变量赋值于另一个变量。

io.Readerio.WriterReaderWriter
// 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)
}
ReadWriteio.Readerio.Writerio.ReaderRead
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
rrio.Readerrio.Reader

接口类型的一个非常重要的例子是空接口:

interface{}

它表示空方法集,并且任何值都满足实现了空接口,因为任何值具有零个或多个方法,而空接口没有方法供实现。

Go

而之所以先重温接口就是因为反射和接口息息相关

二、The representation of an interface

接口类型的变量存储一对儿信息,分别是分配给该变量的具体值以及该值的类型描述符。
例如:

var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
    return nil, err
}
r = tty
r(value, type)(tty, *os.File)rRead
var w io.Writer
w = r.(io.Writer)
rio.Writerww(tty,* os.File)
(value, concrete type)(value, interface type)
concrete typeinterface type

三、关于反射

3.1. Reflection goes from interface value to reflection object.

(type, value)typevaluereflect.TypeOfreflect.ValueOfreflect.Typereflect.ValueTypeOf
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
    fmt.Println("value:", reflect.ValueOf(x))
}

结果输出为:

type: float64
value: 3.4

说明:

type&{1 “Allen.Wu” 25}reflect.Typereflect.Value
reflect.TypeOf
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type
reflect.TypeOf(x)xreflect.TypeOf
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x))
fmt.Println("value:", reflect.ValueOf(x).String())

输出结果为:

value: 3.4
value: <float64 Value>
reflect.Typereflect.ValueValueTypereflect.ValueTypeTypeValueKind

反射库具有几个值得一提的属性。

首先,为使 API 保持简单,Value 的 “getter” 和 “setter” 方法在可以容纳该值的最大类型上运行:例如,所有有符号整数的 int64。 也就是说,Value 的 Int 方法返回一个 int64,而 SetInt 值采用一个 int64; 可能需要转换为涉及的实际类型:

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())     
Kind()
package main

import (
    "fmt"
    "reflect"
)

func main() {
    type MyInt int      // 反射对象包含用户定义的整数类型的值
    var x MyInt = 7
    v := reflect.TypeOf(x)
    fmt.Println(v)
    fmt.Println(v.Kind())
}

则会输出:

main.MyInt
int

3.2. Reflection goes from reflection object to interface value.

Golang 的反射也有其逆向过程。

reflect.ValueInterface()
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}

例如:

func main() {
    var xx float64 = 3.4
    v := reflect.ValueOf(xx)     // v is a reflection object
    y := v.Interface().(float64) // y will have type float64.
    fmt.Println(y)
    fmt.Printf("%T", y)
}

输出结果为:

3.4
float64
interface{}

所以综上述两点可得知,Golang 中的反射可理解为包含两个过程,一个是接口值到反射对象的过程,另一个则是反向的反射对象到接口值的过程。

3.3. To modify a reflection object, the value must be settable.

reflection object
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

如果运行上述这个代码,则会报错提示:

panic: reflect: reflect.Value.SetFloat using unaddressable value

在这个例子中,反射对象 v 的值就是不可设置的,执行下述代码:

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())

则会显示:

settability of v: false

那么什么是可设置的呢,在 Golang 官网原文有这么一句

Settability is determined by whether the reflection object holds the original item.

xreflect.ValueOfxSetFloatxx

而如果我们想要修改其内容,很简单,将对象的指针传入其中,于是刚刚的代码可以改为:

var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())
fmt.Println("----------------")
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(z)

此时输出:

float64type of p: *float64
settability of p: false
settability of v: true
----------------
7.1
7.1

Structs
反射修改内容一个经常使用的地方就是通过指针修改传入的结构体的字段值,只要我们能够获得该结构体对象的指针。

一个简单的示例。

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())
}
typeOfTreflect.Value
0: A int = 23
1: B string = skidoo
TpublicprotectedT
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)

// output is "t is now {77 Sunset Strip}"

四、总结

反射的三条规律:

To modify a reflection object, the value must be settable.

【参考文献】