update tw

This commit is contained in:
chai2010
2015-12-18 10:53:03 +08:00
parent 510c741a6f
commit c66a96ee52
106 changed files with 864 additions and 864 deletions
+6 -6
View File
@@ -1,11 +1,11 @@
## 12.1. 為何需要反射?
## 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 的類型, 這會導緻對這些庫的依賴.
沒有一種方法來檢査未知類型的錶示方式, 我們被卡住了. 這就是我們爲何需要反射的原因.
沒有一種方法來檢査未知類型的表示方式, 我們被卡住了. 這就是我們爲何需要反射的原因.
+11 -11
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,7 +35,7 @@ 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:
@@ -44,7 +44,7 @@ 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
+1 -1
View File
@@ -1,3 +1,3 @@
## 12.6. 示例: 解碼S錶達式
## 12.6. 示例: 解碼S表達式
TODO
+1 -1
View File
@@ -1,3 +1,3 @@
## 12.7. 穫取結構體字段標識
## 12.7. 獲取結構體字段標識
TODO
+2 -2
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都沒有公開反射相關的接口.