The Go Blog Go 博客
The Laws of Reflection
反思法则
Introduction 介绍
Reflection in computing is the ability of a program to examine its own structure,
particularly through types;
it’s a form of metaprogramming.
It’s also a great source of confusion.
计算中的反射是程序检查其自身结构的能力,特别是通过类型;这是元编程的一种形式。这也是造成混乱的一个重要原因。
In this article we attempt to clarify things by explaining how reflection works in Go.
Each language’s reflection model is different (and many languages don’t support it at all),
but this article is about Go, so for the rest of this article the word “reflection”
should be taken to mean “reflection in Go”.
在本文中,我们试图通过解释反射在 Go 中的工作原理来澄清问题。每种语言的反射模型都是不同的(许多语言根本不支持它),但本文是关于 Go 的,因此在本文的其余部分中,“反射”一词应理解为“Go 中的反射”。
Note added January 2022: This blog post was written in 2011 and predates
parametric polymorphism (a.k.a. generics) in Go.
Although nothing important in the article has become incorrect as a result
of that development in the language,
it has been tweaked in a few places to avoid
confusing someone familiar with modern Go.
2022 年 1 月添加的注释:这篇博文写于 2011 年,早于 Go 中的参数多态性(又称泛型)。尽管本文中的任何重要内容都没有因为语言的发展而变得不正确,但它在一些地方进行了调整,以避免让熟悉现代 Go 的人感到困惑。
Types and interfaces 类型和接口
Because reflection builds on the type system, let’s start with a refresher about types in Go.
因为反射建立在类型系统之上,所以让我们首先回顾一下 Go 中的类型。
Go is statically typed. Every variable has a static type,
that is, exactly one type known and fixed at compile time:
int
, float32
, *MyType
, []byte
, and so on. If we declare
Go 是静态类型的。每个变量都有一个静态类型,即在编译时已知并固定的一种类型: int
、 float32
、 *MyType
、 []byte
,等等。如果我们声明
type MyInt int
var i int
var j MyInt
then i
has type int
and j
has type MyInt
.
The variables i
and j
have distinct static types and,
although they have the same underlying type,
they cannot be assigned to one another without a conversion.
那么 i
的类型为 int
, j
的类型为 MyInt
。变量 i
和 j
具有不同的静态类型,尽管它们具有相同的基础类型,但如果不进行转换,它们就无法相互分配。
One important category of type is interface types,
which represent fixed sets of methods.
(When discussing reflection, we can ignore the use of
interface definitions as constraints within polymorphic code.)
An interface variable can store any concrete (non-interface) value as long
as that value implements the interface’s methods.
A well-known pair of examples is io.Reader
and io.Writer
,
the types Reader
and Writer
from the io package:
类型的一个重要类别是接口类型,它表示固定的方法集。 (在讨论反射时,我们可以忽略使用接口定义作为多态代码中的约束。)接口变量可以存储任何具体(非接口)值,只要该值实现接口的方法即可。一对著名的示例是 io.Reader
和 io.Writer
,即来自 io 包的 Reader
和 Writer
类型:
// 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)
}
Any type that implements a Read
(or Write
) method with this signature
is said to implement io.Reader
(or io.Writer
).
For the purposes of this discussion, that means that a variable of type
io.Reader
can hold any value whose type has a Read
method:
任何使用此签名实现 Read
(或 Write
)方法的类型都被称为实现 io.Reader
(或 io.Writer
)。出于本讨论的目的,这意味着 io.Reader
类型的变量可以保存其类型具有 Read
方法的任何值:
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
It’s important to be clear that whatever concrete value r
may hold,
r
’s type is always io.Reader
:
Go is statically typed and the static type of r
is io.Reader
.
重要的是要清楚,无论 r
可能包含什么具体值, r
的类型始终是 io.Reader
:Go 是静态类型,而 r
是 io.Reader
。
An extremely important example of an interface type is the empty interface:
接口类型的一个极其重要的例子是空接口:
interface{}
or its equivalent alias,
或其等效别名,
any
It represents the empty set of methods and is satisfied by any value at all, since every value has zero or more methods.
它表示空的方法集,并且可以满足任何值,因为每个值都有零个或多个方法。
Some people say that Go’s interfaces are dynamically typed,
but that is misleading.
They are statically typed: a variable of interface type always has the same static type,
and even though at run time the value stored in the interface variable may change type,
that value will always satisfy the interface.
有人说 Go 的接口是动态类型的,但这是误导。它们是静态类型的:接口类型的变量始终具有相同的静态类型,即使在运行时存储在接口变量中的值可能会更改类型,该值也将始终满足接口的要求。
We need to be precise about all this because reflection and interfaces are closely related.
我们需要精确地对待这一切,因为反射和接口密切相关。
The representation of an interface
接口的表示
Russ Cox has written a detailed blog post
about the representation of interface values in Go.
It’s not necessary to repeat the full story here,
but a simplified summary is in order.
Russ Cox 撰写了一篇关于 Go 中接口值表示的详细博客文章。这里没有必要重复整个故事,但可以做一个简单的总结。
A variable of interface type stores a pair:
the concrete value assigned to the variable,
and that value’s type descriptor.
To be more precise, the value is the underlying concrete data item that
implements the interface and the type describes the full type of that item. For instance, after
接口类型的变量存储一对:分配给该变量的具体值,以及该值的类型描述符。更准确地说,值是实现接口的底层具体数据项,类型描述了该项的完整类型。例如,之后
var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
r = tty
r
contains, schematically, the (value, type) pair,
(tty
, *os.File
).
Notice that the type *os.File
implements methods other than Read
;
even though the interface value provides access only to the Read
method,
the value inside carries all the type information about that value.
That’s why we can do things like this:
r
示意性地包含(值,类型)对( tty
, *os.File
)。请注意,类型 *os.File
实现了 Read
以外的方法;尽管接口值仅提供对 Read
方法的访问,但内部值携带有关该值的所有类型信息。这就是为什么我们可以这样做:
var w io.Writer
w = r.(io.Writer)
The expression in this assignment is a type assertion;
what it asserts is that the item inside r
also implements io.Writer
,
and so we can assign it to w
.
After the assignment, w
will contain the pair (tty
, *os.File
).
That’s the same pair as was held in r
. The static type of the interface
determines what methods may be invoked with an interface variable,
even though the concrete value inside may have a larger set of methods.
此赋值中的表达式是类型断言;它断言 r
中的项目也实现了 io.Writer
,因此我们可以将其分配给 w
。分配后, w
将包含该对( tty
, *os.File
)。这与 r
中保存的对相同。接口的静态类型决定了可以使用接口变量调用哪些方法,即使内部的具体值可能具有更大的方法集。
Continuing, we can do this:
继续,我们可以这样做:
var empty interface{}
empty = w
and our empty interface value empty
will again contain that same pair,
(tty
, *os.File
).
That’s handy: an empty interface can hold any value and contains all the
information we could ever need about that value.
我们的空接口值 empty
将再次包含同一对( tty
, *os.File
)。这很方便:空接口可以保存任何值,并包含我们可能需要的有关该值的所有信息。
(We don’t need a type assertion here because it’s known statically that
w
satisfies the empty interface.
In the example where we moved a value from a Reader
to a Writer
,
we needed to be explicit and use a type assertion because Writer
’s methods
are not a subset of Reader
’s.)
(这里我们不需要类型断言,因为静态地知道 w
满足空接口。在我们将值从 Reader
移动到 Writer
的方法不是 Reader
的子集。)
One important detail is that the pair inside an interface variable always has the form (value,
concrete type) and cannot have the form (value, interface type).
Interfaces do not hold interface values.
一个重要的细节是,接口变量内的对始终具有(值,具体类型)形式,并且不能具有(值,接口类型)形式。接口不保存接口值。
Now we’re ready to reflect.
现在我们准备好反思了。
The first law of reflection
第一反射定律
1. Reflection goes from interface value to reflection object.
1.反射从接口值到反射对象。
At the basic level, reflection is just a mechanism to examine the type and
value pair stored inside an interface variable.
To get started, there are two types we need to know about in package reflect:
Type and Value.
Those two types give access to the contents of an interface variable,
and two simple functions, called reflect.TypeOf
and reflect.ValueOf
,
retrieve reflect.Type
and reflect.Value
pieces out of an interface value.
(Also, from a reflect.Value
it’s easy to get to the corresponding reflect.Type
,
but let’s keep the Value
and Type
concepts separate for now.)
从根本上来说,反射只是一种检查存储在接口变量内的类型和值对的机制。首先,我们需要了解反射包中的两种类型:类型和值。这两种类型可以访问接口变量的内容,以及两个简单的函数,称为 reflect.TypeOf
和 reflect.ValueOf
,检索 reflect.Type
和 reflect.Value
接口值的一部分。 (此外,从 reflect.Value
很容易到达相应的 reflect.Type
,但现在让我们将 Value
和 Type
概念分开.)
Let’s start with TypeOf
:
让我们从 TypeOf
开始:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
}
This program prints 该程序打印
type: float64
You might be wondering where the interface is here,
since the program looks like it’s passing the float64
variable x
,
not an interface value, to reflect.TypeOf
.
But it’s there; as godoc reports,
the signature of reflect.TypeOf
includes an empty interface:
您可能想知道这里的接口在哪里,因为程序看起来像是将 float64
变量 x
(而不是接口值)传递给 reflect.TypeOf
。但它就在那里;正如 godoc 报告的那样, reflect.TypeOf
的签名包含一个空接口:
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type
When we call reflect.TypeOf(x)
, x
is first stored in an empty interface,
which is then passed as the argument;
reflect.TypeOf
unpacks that empty interface to recover the type information.
当我们调用 reflect.TypeOf(x)
时, x
首先存储在一个空接口中,然后作为参数传递; reflect.TypeOf
解压该空接口以恢复类型信息。
The reflect.ValueOf
function, of course,
recovers the value (from here on we’ll elide the boilerplate and focus just
on the executable code):
当然, reflect.ValueOf
函数会恢复值(从这里开始,我们将省略样板文件,只关注可执行代码):
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x).String())
prints 印刷
value: <float64 Value>
(We call the String
method explicitly because by default the fmt
package
digs into a reflect.Value
to show the concrete value inside.
The String
method does not.)
(我们显式调用 String
方法,因为默认情况下 fmt
包会深入到 reflect.Value
中以显示内部的具体值。 String
方法没有。)
Both reflect.Type
and reflect.Value
have lots of methods to let us examine
and manipulate them.
One important example is that Value
has a Type
method that returns the
Type
of a reflect.Value
.
Another is that both Type
and Value
have a Kind
method that returns
a constant indicating what sort of item is stored:
Uint
, Float64
, Slice
, and so on.
Also methods on Value
with names like Int
and Float
let us grab values
(as int64
and float64
) stored inside:
reflect.Type
和 reflect.Value
都有很多方法让我们检查和操作它们。一个重要的例子是 Value
有一个 Type
方法,它返回 reflect.Value
的 Type
。另一个是 Type
和 Value
都有一个 Kind
方法,该方法返回一个常量,指示存储的项目类型: Uint
、 < b10> 、 Slice
等等。 Value
上具有 Int
和 Float
等名称的方法也让我们获取值(如 int64
和 float64
)存储在里面:
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
prints 印刷
type: float64
kind is float64: true
value: 3.4
There are also methods like SetInt
and SetFloat
but to use them we need
to understand settability,
the subject of the third law of reflection, discussed below.
还有像 SetInt
和 SetFloat
这样的方法,但要使用它们,我们需要了解可设置性,即下面讨论的第三反射定律的主题。
The reflection library has a couple of properties worth singling out.
First, to keep the API simple, the “getter” and “setter” methods of Value
operate on the largest type that can hold the value:
int64
for all the signed integers, for instance.
That is, the Int
method of Value
returns an int64
and the SetInt
value takes an int64
;
it may be necessary to convert to the actual type involved:
反射库有几个值得特别指出的属性。首先,为了保持 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()) // v.Uint returns a uint64.
The second property is that the Kind
of a reflection object describes
the underlying type,
not the static type.
If a reflection object contains a value of a user-defined integer type, as in
第二个属性是反射对象的 Kind
描述的是底层类型,而不是静态类型。如果反射对象包含用户定义的整数类型的值,如
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
the Kind
of v
is still reflect.Int
,
even though the static type of x
is MyInt
, not int
.
In other words, the Kind
cannot discriminate an int
from a MyInt
even
though the Type
can.
v
的 Kind
仍然是 reflect.Int
,即使 x
的静态类型是 MyInt
,而不是 < b5> 。换句话说, Kind
无法区分 int
和 MyInt
,尽管 Type
可以。
The second law of reflection
反射第二定律
2. Reflection goes from reflection object to interface value.
2.反射从反射对象到接口值。
Like physical reflection, reflection in Go generates its own inverse.
就像物理反射一样,Go 中的反射也会产生它自己的逆。
Given a reflect.Value
we can recover an interface value using the Interface
method;
in effect the method packs the type and value information back into an interface
representation and returns the result:
给定一个 reflect.Value
,我们可以使用 Interface
方法恢复接口值;实际上,该方法将类型和值信息打包回接口表示形式并返回结果:
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}
As a consequence we can say
因此我们可以说
y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)
to print the float64
value represented by the reflection object v
.
打印反射对象 v
表示的 float64
值。
We can do even better, though. The arguments to fmt.Println
,
fmt.Printf
and so on are all passed as empty interface values,
which are then unpacked by the fmt
package internally just as we have
been doing in the previous examples.
Therefore all it takes to print the contents of a reflect.Value
correctly
is to pass the result of the Interface
method to the formatted print routine:
不过,我们可以做得更好。 fmt.Println
、 fmt.Printf
等的参数都作为空接口值传递,然后由 fmt
包在内部解包,就像我们一直在做的那样在前面的例子中。因此,正确打印 reflect.Value
的内容所需要做的就是将 Interface
方法的结果传递给格式化的打印例程:
fmt.Println(v.Interface())
(Since this article was first written, a change was made to the fmt
package so that it automatically unpacks a reflect.Value
like this, so
we could just say
(自从本文首次撰写以来,对 fmt
包进行了更改,以便它自动解压像这样的 reflect.Value
,所以我们可以说
fmt.Println(v)
for the same result, but for clarity we’ll keep the .Interface()
calls
here.)
获得相同的结果,但为了清楚起见,我们将在此处保留 .Interface()
调用。)
Since our value is a float64
,
we can even use a floating-point format if we want:
由于我们的值是 float64
,如果需要,我们甚至可以使用浮点格式:
fmt.Printf("value is %7.1e\n", v.Interface())
and get in this case
在这种情况下
3.4e+00
Again, there’s no need to type-assert the result of v.Interface()
to float64
;
the empty interface value has the concrete value’s type information inside
and Printf
will recover it.
同样,不需要将 v.Interface()
的结果类型断言到 float64
;空接口值内部有具体值的类型信息, Printf
将恢复它。
In short, the Interface
method is the inverse of the ValueOf
function,
except that its result is always of static type interface{}
.
简而言之, Interface
方法是 ValueOf
函数的逆函数,只不过它的结果始终是静态类型 interface{}
。
Reiterating: Reflection goes from interface values to reflection objects and back again.
重申一下:反射从接口值到反射对象,然后再返回。
The third law of reflection
反思第三定律
3. To modify a reflection object, the value must be settable.
3. 要修改反射对象,该值必须是可设置的。
The third law is the most subtle and confusing, but it’s easy enough to understand if we start from first principles.
第三定律是最微妙和最令人困惑的,但如果我们从第一原理开始,它就很容易理解。
Here is some code that does not work, but is worth studying.
这是一些不起作用的代码,但值得研究。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
If you run this code, it will panic with the cryptic message
如果您运行此代码,它将因神秘消息而出现恐慌
panic: reflect.Value.SetFloat using unaddressable value
The problem is not that the value 7.1
is not addressable;
it’s that v
is not settable.
Settability is a property of a reflection Value
,
and not all reflection Values
have it.
问题不在于值 7.1
不可寻址;而是值 7.1
不可寻址。就是 v
不可设置。可设置性是反射 Value
的一个属性,并非所有反射 Values
都具有它。
The CanSet
method of Value
reports the settability of a Value
; in our case,
Value
的 CanSet
方法报告 Value
的可设置性;在我们的例子中,
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
prints 印刷
settability of v: false
It is an error to call a Set
method on a non-settable Value
. But what is settability?
在不可设置的 Value
上调用 Set
方法是错误的。但什么是可调整性呢?
Settability is a bit like addressability, but stricter.
It’s the property that a reflection object can modify the actual storage
that was used to create the reflection object.
Settability is determined by whether the reflection object holds the original item. When we say
可设置性有点像可寻址性,但更严格。这是反射对象可以修改用于创建反射对象的实际存储的属性。可设置性取决于反射对象是否持有原始项目。当我们说
var x float64 = 3.4
v := reflect.ValueOf(x)
we pass a copy of x
to reflect.ValueOf
,
so the interface value created as the argument to reflect.ValueOf
is a
copy of x
, not x
itself.
Thus, if the statement
我们将 x
的副本传递给 reflect.ValueOf
,因此作为 reflect.ValueOf
参数创建的接口值是 x
的副本,而不是 x
本身。因此,如果声明
v.SetFloat(7.1)
were allowed to succeed, it would not update x
,
even though v
looks like it was created from x
.
Instead, it would update the copy of x
stored inside the reflection value
and x
itself would be unaffected.
That would be confusing and useless, so it is illegal,
and settability is the property used to avoid this issue.
如果允许成功,它不会更新 x
,即使 v
看起来像是从 x
创建的。相反,它会更新存储在反射值内的 x
副本,而 x
本身不受影响。这会令人困惑且无用,因此它是非法的,而可设置性是用于避免此问题的属性。
If this seems bizarre, it’s not. It’s actually a familiar situation in unusual garb.
Think of passing x
to a function:
如果这看起来很奇怪,其实不然。这实际上是一种穿着不寻常服装的熟悉情况。考虑将 x
传递给函数:
f(x)
We would not expect f
to be able to modify x
because we passed a copy
of x
’s value, not x
itself.
If we want f
to modify x
directly we must pass our function the address
of x
(that is, a pointer to x
):
我们不希望 f
能够修改 x
,因为我们传递了 x
值的副本,而不是 x
本身。如果我们希望 f
直接修改 x
,我们必须向函数传递 x
的地址(即指向 x
的指针) :
f(&x)
This is straightforward and familiar, and reflection works the same way.
If we want to modify x
by reflection, we must give the reflection library
a pointer to the value we want to modify.
这是简单且熟悉的,反射的工作方式也是如此。如果我们想通过反射修改 x
,我们必须给反射库一个指向我们想要修改的值的指针。
Let’s do that. First we initialize x
as usual and then create a reflection value that points to it, called p
.
让我们这样做吧。首先,我们像往常一样初始化 x
,然后创建一个指向它的反射值,称为 p
。
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())
The output so far is
到目前为止的输出是
type of p: *float64
settability of p: false
The reflection object p
isn’t settable,
but it’s not p
we want to set, it’s (in effect) *p
.
To get to what p
points to, we call the Elem
method of Value
,
which indirects through the pointer, and save the result in a reflection Value
called v
:
反射对象 p
不可设置,但它不是我们想要设置的 p
,它(实际上)是 *p
。为了获取 p
指向的内容,我们调用 Value
的 Elem
方法,该方法通过指针间接进行,并将结果保存在反射 Value
称为 v
:
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())
Now v
is a settable reflection object, as the output demonstrates,
现在 v
是一个可设置的反射对象,如输出所示,
settability of v: true
and since it represents x
, we are finally able to use v.SetFloat
to modify the value of x
:
由于它代表 x
,我们终于可以使用 v.SetFloat
来修改 x
的值:
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
The output, as expected, is
正如预期的那样,输出是
7.1
7.1
Reflection can be hard to understand but it’s doing exactly what the language does,
albeit through reflection Types
and Values
that can disguise what’s going on.
Just keep in mind that reflection Values need the address of something in
order to modify what they represent.
反射可能很难理解,但它所做的正是语言所做的事情,尽管通过反射 Types
和 Values
可以掩盖正在发生的事情。请记住,反射值需要某些内容的地址才能修改它们所代表的内容。
Structs 结构体
In our previous example v
wasn’t a pointer itself,
it was just derived from one.
A common way for this situation to arise is when using reflection to modify
the fields of a structure.
As long as we have the address of the structure,
we can modify its fields.
在我们前面的例子中 v
本身并不是一个指针,它只是从一个指针派生出来的。出现这种情况的常见方式是使用反射来修改结构体的字段。只要我们有结构体的地址,我们就可以修改它的字段。
Here’s a simple example that analyzes a struct value, t
.
We create the reflection object with the address of the struct because we’ll
want to modify it later.
Then we set typeOfT
to its type and iterate over the fields using straightforward
method calls (see package reflect for details).
Note that we extract the names of the fields from the struct type,
but the fields themselves are regular reflect.Value
objects.
这是一个分析结构体值 t
的简单示例。我们使用结构体的地址创建反射对象,因为我们稍后需要修改它。然后我们将 typeOfT
设置为它的类型,并使用简单的方法调用迭代字段(有关详细信息,请参阅包reflect)。请注意,我们从结构类型中提取字段的名称,但字段本身是常规的 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())
}
The output of this program is
该程序的输出是
0: A int = 23
1: B string = skidoo
There’s one more point about settability introduced in passing here:
the field names of T
are upper case (exported) because only exported fields
of a struct are settable.
这里还引入了关于可设置性的一点: T
的字段名称是大写的(导出的),因为只有结构体的导出字段是可设置的。
Because s
contains a settable reflection object, we can modify the fields of the structure.
因为 s
包含一个可设置的反射对象,所以我们可以修改该结构体的字段。
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)
And here’s the result:
结果如下:
t is now {77 Sunset Strip}
If we modified the program so that s
was created from t
,
not &t
, the calls to SetInt
and SetString
would fail as the fields
of t
would not be settable.
如果我们修改程序,使 s
是从 t
创建的,而不是 &t
,则对 SetInt
和 SetString
的字段不可设置。
Conclusion 结论
Here again are the laws of reflection:
这又是反射定律:
-
Reflection goes from interface value to reflection object.
反射从接口值到反射对象。 -
Reflection goes from reflection object to interface value.
反射从反射对象到接口值。 -
To modify a reflection object, the value must be settable.
要修改反射对象,该值必须是可设置的。
Once you understand these laws reflection in Go becomes much easier to use,
although it remains subtle.
It’s a powerful tool that should be used with care and avoided unless strictly necessary.
一旦你理解了这些法则,Go 中的反射就会变得更容易使用,尽管它仍然很微妙。这是一个强大的工具,应谨慎使用,除非绝对必要,否则应避免使用。
There’s plenty more to reflection that we haven’t covered — sending and
receiving on channels,
allocating memory, using slices and maps,
calling methods and functions — but this post is long enough.
We’ll cover some of those topics in a later article.
还有很多关于反射的内容我们没有涉及——在通道上发送和接收、分配内存、使用切片和映射、调用方法和函数——但是这篇文章已经足够长了。我们将在后面的文章中介绍其中一些主题。
Next article: The Go image package
下一篇:Go镜像包
Previous article: Two Go Talks: "Lexical Scanning in Go" and "Cuddle: an App Engine Demo"
上一篇文章: 两场 Go 讲座:“Go 中的词法扫描”和“Cuddle:App Engine 演示”
Blog Index 博客索引