Reflection in Go
Contents
Why Reflection
Sometimes we need to write a function capable of dealing uniformly with values of types that don’t satisfy a common interface, don’t have a known representation, or don’t exist at the time we design the function—or even all three.
- type switch 不可能枚举所有的类型,就算可能也不应该这样做,否则会出现使用 type switch 的实现的公共库反倒要依赖调用它的库里定义的类型的情况。
Go provides a mechanism called reflection to update variables and inspect their values at run time, to call their methods, and to apply the operations intrinsic to their representation, all without knowing their types at compile time.
fmt
,encoding/json
,encoding/xml
,text/template
,html/template
, 这些包的实现都依赖于反射。- Reflection is provided by the
reflect
package.
Reflection is complex to reason about and not for casual use. Where possible, you should avoid exposing reflection in the API of a package.
reflect.Type
reflect.Type
表示 Go 的一种类型,它是接口类型,包含许多方法来区分类型、检查组成(如结构体字段、函数参数等),对它的唯一实现就是 type descriptor。
reflect.TypeOf
接收一个 interface{}
类型的参数,返回的是参数的动态类型(具体类型的入参被转成的 interface value),即 reflect.Type
类型。
reflect.Type
is capable of representing interface types too.
t := reflect.TypeOf(3)
fmt.Println(t.String()) // "int"
fmt.Printf("%T\n", 3) // "int"
fmt.Println(t) // "int"
- An assignment from a concrete value to an interface type performs an implicit interface conversion, which creates an interface value consisting of two components: its dynamic type is the operand’s type (
int
) and its dynamic value is the operand’s value (3
). reflect.Type
满足fmt.Stringer
接口,String()
返回的是它的 interface value 动态类型;fmt.Printf("%T")
是这一过程的简写。
var w io.Writer = os.Stdout
fmt.Println(reflect.TypeOf(w)) // "*os.File", not "io.Writer"
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
func NewFile(fd uintptr, name string) *File
- Because
reflect.TypeOf
returns an interface value’s dynamic type, it always returns a concrete type.
reflect.Value
A reflect.Value
can hold a value of any type.
reflect.ValueOf
接收一个 interface{}
类型的参数,返回一个包含参数的动态值的 reflect.Value
。
- As with
reflect.TypeOf
, the results ofreflect.ValueOf
are always concrete, but areflect.Value
can hold interface values too.
v := reflect.ValueOf(3)
fmt.Println(v) // "3"
fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // "<int Value>"
t := v.Type()
fmt.Println(t.String()) // "int"
reflect.Value
满足fmt.Stringer
接口,但只有reflect.Value
装的是字符串时会返回具体的值,其它情况都只会返回类型。fmt.Printf("%v")
会打印值,它对reflect.Values
做了特殊处理。- Calling the
Type
method on areflect.Value
returns its type as areflect.Type
.
v := reflect.ValueOf(3)
x := v.Interface() // an interface{}
i := x.(int) // an int
fmt.Printf("%d\n", i) // "3"
- The inverse operation to
reflect.ValueOf
is thereflect.Value.Interface
method. It returns aninterface{}
holding the same concrete value as thereflect.Value
reflect.Value
和 interface{}
都能容纳任意值,区别是 interface{}
隐藏了值的 representation and intrinsic operations,也不暴露出任何方法,因此只有知道它的动态类型再去断言才能用起来,而 reflect.Value
有很多方法可以用来检查它的内容。
- 见以下 <code>Display</code> 示例。
- Unexported fields are visible to reflection.
func (v Value) Kind() Kind
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
reflect.Value
的Kind()
方法可以返回种类。Kind
is concerned only with the underlying representation.
- 类型是无穷的(所以断言行不通),但类型的种类是有限的。
- The basic types
Bool
,String
, and all the numbers; the aggregate typesArray
andStruct
; the reference typesChan
,Func
,Ptr
,Slice
, andMap
;Interface
types; and finallyInvalid
, meaning no value at all. - The zero value of a
reflect.Value
has kindInvalid
.
- The basic types
Setting Variables with reflect.Value
Some reflect.Values
s are addressable; others are not.
x := 2 // value type variable
a := reflect.ValueOf(2) // 2 int no
b := reflect.ValueOf(x) // 2 int no
c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes (x)
- The value within
a
is a copy of the integer 2. The same is true ofb
. The value withinc
is a copy of the pointer value&x
. d
, derived fromc
by dereferencing the pointer within it, refers to a variable and is thus addressable.- No
reflect.Value
returned byreflect.ValueOf(x)
is addressable. We can use this approach, callingreflect.ValueOf(&x).Elem()
, to obtain an addressableValue
for any variablex
. We obtain an addressablereflect.Value
whenever we indirect through a pointer, even if we started from a non-addressableValue
.- For example, since the slice indexing expression
e[i]
implicitly follows a pointer, it is addressable even if the expressione
is not. By analogy,reflect.ValueOf(e).Index(i)
refers to a variable, and is thus addressable even ifreflect.ValueOf(e)
is not.
- For example, since the slice indexing expression
x := 2
d := reflect.ValueOf(&x).Elem() // d refers to the variable x
px := d.Addr().Interface().(*int) // px := &x
*px = 3 // x = 3
fmt.Println(x) // "3"
d.Set(reflect.ValueOf(4))
fmt.Println(x) // "4"
Application
formatAtom
formatAtom
treats each value as an indivisible thing with no internal structure.
// https://go.dev/play/p/9uLndi_lrNB
// Any formats any value as a string.
func Any(value interface{}) string {
return formatAtom(reflect.ValueOf(value))
}
// formatAtom formats a value without inspecting its internal structure.
func formatAtom(v reflect.Value) string {
switch v.Kind() {
case reflect.Invalid:
return "invalid"
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(v.Uint(), 10)
// ...floating-point and complex cases omitted for brevity...
case reflect.Bool:
return strconv.FormatBool(v.Bool())
case reflect.String:
return strconv.Quote(v.String())
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
return v.Type().String() + " 0x" +
strconv.FormatUint(uint64(v.Pointer()), 16)
default: // reflect.Array, reflect.Struct, reflect.Interface
return v.Type().String() + " value"
}
}
var x int64 = 1
var d time.Duration = 1 * time.Nanosecond
fmt.Println(Any(x)) // "1"
fmt.Println(Any(d)) // "1"
fmt.Println(Any([]int64{x})) // "[]int64 0xc0000b2008"
fmt.Println(Any([]time.Duration{d})) // "[]time.Duration 0xc0000b2020"
- For aggregate types (structs and arrays) and interfaces it prints only the type of the value.
- For reference types (channels, functions, pointers, slices, and maps), it prints the type and the reference address in hexadecimal.
Display
Given an arbitrarily complex value x
, Display
prints the complete structure of that value, labeling each element with the path by which it was found.
func Display(name string, x interface{}) {
fmt.Printf("Display %s (%T):\n", name, x)
display(name, reflect.ValueOf(x))
}
func display(path string, v reflect.Value) {
switch v.Kind() {
case reflect.Invalid:
fmt.Printf("%s = invalid\n", path)
case reflect.Slice, reflect.Array:
for i := 0; i < v.Len(); i++ {
display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
}
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
display(fieldPath, v.Field(i))
}
case reflect.Map:
for _, key := range v.MapKeys() {
display(fmt.Sprintf("%s[%s]", path,
formatAtom(key)), v.MapIndex(key))
}
case reflect.Ptr:
if v.IsNil() {
fmt.Printf("%s = nil\n", path)
} else {
display(fmt.Sprintf("(*%s)", path), v.Elem())
}
case reflect.Interface:
if v.IsNil() {
fmt.Printf("%s = nil\n", path)
} else {
fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
display(path+".value", v.Elem())
}
default: // basic types, channels, funcs
fmt.Printf("%s = %s\n", path, formatAtom(v))
}
}
strangelove := Movie{
Title: "Dr. Strangelove",
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
Year: 1964,
Color: false,
Actor: map[string]string{
"Dr. Strangelove": "Peter Sellers",
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
"Pres. Merkin Muffley": "Peter Sellers",
"Gen. Buck Turgidson": "George C. Scott",
"Brig. Gen. Jack D. Ripper": "Sterling Hayden",
`Maj. T.J. "King" Kong`: "Slim Pickens",
},
Oscars: []string{
"Best Actor (Nomin.)",
"Best Adapted Screenplay (Nomin.)",
"Best Director (Nomin.)",
"Best Picture (Nomin.)",
},
}
Display("strangelove", strangelove)
// Display strangelove (main.Movie):
// strangelove.Title = "Dr. Strangelove"
// strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"
// strangelove.Year = 1964
// strangelove.Color = false
// strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"
// strangelove.Actor["Maj. T.J. \"King\" Kong"] = "Slim Pickens"
// strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"
// strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"
// strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"
// strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"
// strangelove.Oscars[0] = "Best Actor (Nomin.)"
// strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"
// strangelove.Oscars[2] = "Best Director (Nomin.)"
// strangelove.Oscars[3] = "Best Picture (Nomin.)"
// strangelove.Sequel = nil
Display("os.Stderr", os.Stderr)
// Display os.Stderr (*os.File):
// (*(*os.Stderr).file).pfd.fdmu.state = 0
// (*(*os.Stderr).file).pfd.fdmu.rsema = 0
// (*(*os.Stderr).file).pfd.fdmu.wsema = 0
// (*(*os.Stderr).file).pfd.Sysfd = 2
// (*(*os.Stderr).file).pfd.pd.runtimeCtx = 0
// (*(*os.Stderr).file).pfd.iovecs = nil
// (*(*os.Stderr).file).pfd.csema = 0
// (*(*os.Stderr).file).pfd.isBlocking = 1
// (*(*os.Stderr).file).pfd.IsStream = true
// (*(*os.Stderr).file).pfd.ZeroReadIsEOF = true
// (*(*os.Stderr).file).pfd.isFile = true
// (*(*os.Stderr).file).name = "/dev/stderr"
// (*(*os.Stderr).file).dirinfo = nil
// (*(*os.Stderr).file).nonblock = false
// (*(*os.Stderr).file).stdoutOrErr = true
// (*(*os.Stderr).file).appendMode = false
// the internal representation of the type descriptor for *os.File
Display("rV", reflect.ValueOf(os.Stderr))
// Display rV (reflect.Value):
// (*rV.typ).size = 8
// (*rV.typ).ptrdata = 8
// (*rV.typ).hash = 871609668
// (*rV.typ).tflag = 9
// (*rV.typ).align = 8
// (*rV.typ).fieldAlign = 8
// (*rV.typ).kind = 54
// (*rV.typ).equal = func(unsafe.Pointer, unsafe.Pointer) bool 0x10032e0
// (*(*rV.typ).gcdata) = 1
// (*rV.typ).str = 6509
// (*rV.typ).ptrToThis = 0
// rV.ptr = unsafe.Pointer value
// rV.flag = 22
var i interface{} = 3
Display("i", i)
// Display i (int):
// i = 3
Display("&i", &i)
// Display &i (*interface {}):
// (*&i).type = int
// (*&i).value = 3
reflect.Slice
,reflect.Array
:Len()
,Index()
reflect.Struct
:NumField()
,Field()
reflect.Ptr
:IsNil()
,Elem()
- Cannot handle cycles in the object graph.
References