This commit is contained in:
chai2010
2015-12-14 11:31:28 +08:00
parent 44532b45b5
commit 7b4e9340f8
7 changed files with 82 additions and 82 deletions

View File

@@ -1,11 +1,11 @@
## 12.1. 為何需要反射?
候我需要编写一个函数能够处理一类并不满足普通公共接口的型的值, 也可能它们并没有确定的示方式, 或者在我们设计该函数的时候还这些类型可能不存在, 各种情况都有可能.
候我需要編寫一箇函數能夠處理一類併不滿足普通公共接口的型的值, 也可能它們併沒有確定的示方式, 或者在我們設計該函數的時候還這些類型可能不存在, 各種情況都有可能.
个大家熟悉的例子是 fmt.Fprintf 函提供的字符串格式化处理逻辑, 它可以用例任意型的值格式化打印, 甚至是用自定义的类型. 让我们来尝试实现一个类似功能的函. 简单起见, 我的函数只接收一个参数, 然后返回和 fmt.Sprint 似的格式化的字符串, 我的函名也叫 Sprint.
箇大傢熟悉的例子是 fmt.Fprintf 函提供的字符串格式化處理邏輯, 它可以用例任意型的值格式化打印, 甚至是用自定義的類型. 讓我們來嘗試實現一箇類似功能的函. 簡單起見, 我的函數隻接收一箇參數, 然後返迴和 fmt.Sprint 似的格式化的字符串, 我的函名也叫 Sprint.
使用了 switch 分支首先来测试输入参数是否实现了 String 方法, 如果是的就使用方法. 然后继续增加测试分支, 检查是否是每个基于 string, int, bool 等基础类型的动态类型, 在每种情况下执行适当的格式化操作.
使用了 switch 分支首先來測試輸入參數是否實現了 String 方法, 如果是的就使用方法. 然後繼續增加測試分支, 檢査是否是每箇基於 string, int, bool 等基礎類型的動態類型, 在每種情況下執行適噹的格式化操作.
```Go
func Sprint(x interface{}) string {
@@ -32,8 +32,8 @@ func Sprint(x interface{}) string {
}
```
但是我如何理其它似 []float64, map[string][]string 等型呢? 我们当然可以添加更多的测试分支, 但是这些组合类型的目基本是无穷的. 有如何理 url.Values 等命令的型呢? 虽然类型分支可以识别出底层的基础类型是 map[string][]string, 但是它不匹配 url.Values 型, 因为这是两种不同的类型, 而且 switch 分支也不可能包含每个类似 url.Values 的型, 这会导致对这些库的依.
但是我如何理其它似 []float64, map[string][]string 等型呢? 我們噹然可以添加更多的測試分支, 但是這些組閤類型的目基本是無窮的. 有如何理 url.Values 等命令的型呢? 雖然類型分支可以識別齣底層的基礎類型是 map[string][]string, 但是它不匹配 url.Values 型, 因爲這是兩種不衕的類型, 而且 switch 分支也不可能包含每箇類似 url.Values 的型, 這會導緻對這些庫的依.
有一方法来检查未知型的示方式, 我被卡住了. 就是我们为何需要反射的原因.
有一方法來檢査未知型的示方式, 我被卡住了. 就是我們爲何需要反射的原因.

View File

@@ -1,9 +1,9 @@
## 12.2. reflect.Type和reflect.Value
反射是由 reflect 包提供支持. 它定义了两个重要的型, Type 和 Value. 一 Type 示一Go型. 它是一接口, 有多方法来区分类型和检查它们的组件, 例如一个结构体的成或一个函数的参数等. 唯一能反映 reflect.Type 实现的是接口的型描述信息(§7.5), 同样的实体标识了动态类型的接口值.
反射是由 reflect 包提供支持. 它定義了兩箇重要的型, Type 和 Value. 一 Type 示一Go型. 它是一接口, 有多方法來區分類型和檢査它們的組件, 例如一箇結構體的成或一箇函數的參數等. 唯一能反映 reflect.Type 實現的是接口的型描述信息(§7.5), 衕樣的實體標識了動態類型的接口值.
reflect.TypeOf 接受任意的 interface{} 型, 并返回对应动态类型的reflect.Type:
reflect.TypeOf 接受任意的 interface{} 型, 併返迴對應動態類型的reflect.Type:
```Go
t := reflect.TypeOf(3) // a reflect.Type
@@ -11,22 +11,22 @@ fmt.Println(t.String()) // "int"
fmt.Println(t) // "int"
```
其中 TypeOf(3) 调用将值 3 作 interface{} 类型参数传入. 到 7.5将一个具体的值转为接口类型会有一个隐式的接口转换操作, 它会创建一包含两个信息的接口值: 操作数的动态类型(这里是int)和它的动态的值(这里是3).
其中 TypeOf(3) 調用將值 3 作 interface{} 類型參數傳入. 到 7.5將一箇具體的值轉爲接口類型會有一箇隱式的接口轉換操作, 它會創建一包含兩箇信息的接口值: 操作數的動態類型(這裡是int)和它的動態的值(這裡是3).
reflect.TypeOf 返的是一个动态类型的接口值, 它是返回具体的类型. 因此, 下的代码将打印 "*os.File" 而不是 "io.Writer". 稍, 我们将看到 reflect.Type 是具有识别接口型的表达方式功能的.
reflect.TypeOf 返的是一箇動態類型的接口值, 它是返迴具體的類型. 因此, 下的代碼將打印 "*os.File" 而不是 "io.Writer". 稍, 我們將看到 reflect.Type 是具有識別接口型的錶達方式功能的.
```Go
var w io.Writer = os.Stdout
fmt.Println(reflect.TypeOf(w)) // "*os.File"
```
意的是 reflect.Type 接口是足 fmt.Stringer 接口的. 因打印动态类型值对于调试和日是有助的, fmt.Printf 提供了一个简短的 %T 标志参数, 部使用 reflect.TypeOf 的结果输出:
意的是 reflect.Type 接口是滿足 fmt.Stringer 接口的. 因打印動態類型值對於調試和日是有助的, fmt.Printf 提供了一箇簡短的 %T 標誌參數, 部使用 reflect.TypeOf 的結果輸齣:
```Go
fmt.Printf("%T\n", 3) // "int"
```
reflect 包中另一重要的型是 Value. 一 reflect.Value 可以持有一任意型的值. 函 reflect.ValueOf 接受任意的 interface{} 型, 并返回对应动态类型的reflect.Value. 和 reflect.TypeOf 似, reflect.ValueOf 返回的结果也是对于具体的类型, 但是 reflect.Value 也可以持有一接口值.
reflect 包中另一重要的型是 Value. 一 reflect.Value 可以持有一任意型的值. 函 reflect.ValueOf 接受任意的 interface{} 型, 併返迴對應動態類型的reflect.Value. 和 reflect.TypeOf 似, reflect.ValueOf 返迴的結果也是對於具體的類型, 但是 reflect.Value 也可以持有一接口值.
```Go
v := reflect.ValueOf(3) // a reflect.Value
@@ -35,16 +35,16 @@ fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // NOTE: "<int Value>"
```
和 reflect.Type 似, reflect.Value 也足 fmt.Stringer 接口, 但是除非 Value 持有的是字符串, 否 String 是返回具体的类型. 相, 使用 fmt 包的 %v 标志参数, 使用 reflect.Values 的果格式化.
和 reflect.Type 似, reflect.Value 也滿足 fmt.Stringer 接口, 但是除非 Value 持有的是字符串, 否 String 是返迴具體的類型. 相, 使用 fmt 包的 %v 標誌參數, 使用 reflect.Values 的果格式化.
用 Value 的 Type 方法将返回具体类型所对应的 reflect.Type:
調用 Value 的 Type 方法將返迴具體類型所對應的 reflect.Type:
```Go
t := v.Type() // a reflect.Type
fmt.Println(t.String()) // "int"
```
逆操作是用 reflect.ValueOf 对应的 reflect.Value.Interface 方法. 它返回一个 interface{} 类型表示 reflect.Value 对应类型的具值:
逆操作是調用 reflect.ValueOf 對應的 reflect.Value.Interface 方法. 它返迴一箇 interface{} 類型錶示 reflect.Value 對應類型的具值:
```Go
v := reflect.ValueOf(3) // a reflect.Value
@@ -53,9 +53,9 @@ i := x.(int) // an int
fmt.Printf("%d\n", i) // "3"
```
reflect.Value 和 interface{} 都能保存任意的值. 所不的是, 一空的接口藏了值对应的表示方式和所有的公的方法, 因此有我知道具体的动态类型才能使用类型断言来访问内部的值(就像上面那样), 对于内部值并没有特可做的事情. 相比之下, 一 Value 有很多方法来检查其内容, 无论它的具体类型是什. 让我们再次尝试实现我们的格式化函 format.Any.
reflect.Value 和 interface{} 都能保存任意的值. 所不的是, 一空的接口藏了值對應的錶示方式和所有的公的方法, 因此有我知道具體的動態類型纔能使用類型斷言來訪問內部的值(就像上麫那樣), 對於內部值併沒有特可做的事情. 相比之下, 一 Value 有很多方法來檢査其內容, 無論它的具體類型是什. 讓我們再次嘗試實現我們的格式化函 format.Any.
使用 reflect.Value 的 Kind 方法替代之前的型 switch. 虽然还是有无穷多的型, 但是它的kinds类型却是有限的: Bool, String 和 所有数字类型的基础类型; Array 和 Struct 对应的聚合类型; Chan, Func, Ptr, Slice, 和 Map 对应的引用似; 接口型; 还有表示空值的无效类型. (空的 reflect.Value 对应 Invalid 无效类型.)
使用 reflect.Value 的 Kind 方法替代之前的型 switch. 雖然還是有無窮多的型, 但是它的kinds類型卻是有限的: Bool, String 和 所有數字類型的基礎類型; Array 和 Struct 對應的聚閤類型; Chan, Func, Ptr, Slice, 和 Map 對應的引用似; 接口型; 還有錶示空值的無效類型. (空的 reflect.Value 對應 Invalid 無效類型.)
```Go
gopl.io/ch12/format
@@ -96,7 +96,7 @@ func formatAtom(v reflect.Value) string {
}
```
到目前未知, 我的函数将每个值视作一不可分割没有内部结构的, 因此它叫 formatAtom. 对于聚合类型(结构体和数组)个接口是打印型的值, 对于引用型(channels, functions, pointers, slices, 和 maps), 它十六进制打印型的引用地址. 虽然还不够理想, 但是依然是一重大的步, 且 Kind 只关心底层表示, format.Any 也支持新命名的型. 例如:
到目前未知, 我的函數將每箇值視作一不可分割沒有內部結構的, 因此它叫 formatAtom. 對於聚閤類型(結構體和數組)箇接口是打印型的值, 對於引用型(channels, functions, pointers, slices, 和 maps), 它十六進製打印型的引用地址. 雖然還不夠理想, 但是依然是一重大的步, 且 Kind 隻關心底層錶示, format.Any 也支持新命名的型. 例如:
```Go
var x int64 = 1

View File

@@ -1,6 +1,6 @@
# 第十二章 反射
Go提供了一种机制在运行时更新量和检查它们的值, 用它的方法, 和它支持的在操作, 但是在编译时并不知道这些变量的型. 这种机制被称为反射. 反射也可以让我们将类型本身作第一的值类型处理.
Go提供了一種機製在運行時更新量和檢査它們的值, 調用它的方法, 和它支持的在操作, 但是在編譯時併不知道這些變量的型. 這種機製被稱爲反射. 反射也可以讓我們將類型本身作第一的值類型處理.
在本章, 我们将探讨Go言的反射特性, 看看它可以给语言增加哪些表达力, 以及在两个至关重要的API是如何用反射机制的: 一是 fmt 包提供的字符串格式功能, 另一个是类似 encoding/json 和 encoding/xml 提供的针对特定协议的编解码功能. 对于我们在4.6中看到的 text/template 和 html/template 包, 它们的实现也是依反射技的. 然, 反射是一个复杂的内省技, 而应该随意使用, 因此, 管上面这些包都是用反射技术实现的, 但是它自己的API都有公反射相的接口.
在本章, 我們將探討Go言的反射特性, 看看它可以給語言增加哪些錶達力, 以及在兩箇至關重要的API是如何用反射機製的: 一是 fmt 包提供的字符串格式功能, 另一箇是類似 encoding/json 和 encoding/xml 提供的鍼對特定協議的編解碼功能. 對於我們在4.6中看到的 text/template 和 html/template 包, 它們的實現也是依反射技的. 然, 反射是一箇復雜的內省技, 而應該隨意使用, 因此, 管上麫這些包都是用反射技術實現的, 但是它自己的API都有公反射相的接口.