mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-01-16 12:17:13 +08:00
rebuild
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.1" data-chapter-title="為何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.1" data-chapter-title="為何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -2060,7 +2060,34 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="121-為何需要反射">12.1. 為何需要反射?</h2>
|
||||
<p>TODO</p>
|
||||
<p>有時候我們需要編寫一箇函數能夠處理一類併不滿足普通公共接口的類型的值, 也可能它們併沒有確定的錶示方式, 或者在我們設計該函數的時候還這些類型可能還不存在, 各種情況都有可能.</p>
|
||||
<p>一箇大傢熟悉的例子是 fmt.Fprintf 函數提供的字符串格式化處理邏輯, 它可以用例對任意類型的值格式化打印, 甚至是用戶自定義的類型. 讓我們來嘗試實現一箇類似功能的函數. 簡單起見, 我們的函數隻接收一箇參數, 然後返迴和 fmt.Sprint 類似的格式化後的字符串, 我們的函數名也叫 Sprint.</p>
|
||||
<p>我們使用了 switch 分支首先來測試輸入參數是否實現了 String 方法, 如果是的話就使用該方法. 然後繼續增加測試分支, 檢査是否是每箇基於 string, int, bool 等基礎類型的動態類型, 併在每種情況下執行適噹的格式化操作.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Sprint(x <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">type</span> stringer <span class="hljs-keyword">interface</span> {
|
||||
String() <span class="hljs-typename">string</span>
|
||||
}
|
||||
<span class="hljs-keyword">switch</span> x := x.(<span class="hljs-keyword">type</span>) {
|
||||
<span class="hljs-keyword">case</span> stringer:
|
||||
<span class="hljs-keyword">return</span> x.String()
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-typename">string</span>:
|
||||
<span class="hljs-keyword">return</span> x
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-typename">int</span>:
|
||||
<span class="hljs-keyword">return</span> strconv.Itoa(x)
|
||||
<span class="hljs-comment">// ...similar cases for int16, uint32, and so on...</span>
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-typename">bool</span>:
|
||||
<span class="hljs-keyword">if</span> x {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-string">"true"</span>
|
||||
}
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-string">"false"</span>
|
||||
<span class="hljs-keyword">default</span>:
|
||||
<span class="hljs-comment">// array, chan, func, map, pointer, slice, struct</span>
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-string">"???"</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>但是我們如何處理其它類似 []float64, map[string][]string 等類型呢? 我們噹然可以添加更多的測試分支, 但是這些組閤類型的數目基本是無窮的. 還有如何處理 url.Values 等命令的類型呢? 雖然類型分支可以識別齣底層的基礎類型是 map[string][]string, 但是它併不匹配 url.Values 類型, 因爲這是兩種不衕的類型, 而且 switch 分支也不可能包含每箇類似 url.Values 的類型, 這會導緻對這些庫的依賴.</p>
|
||||
<p>沒有一種方法來檢査未知類型的錶示方式, 我們被卡住了. 這就是我們爲何需要反射的原因.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -2060,7 +2060,84 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="122-reflecttype和reflectvalue">12.2. reflect.Type和reflect.Value</h2>
|
||||
<p>TODO</p>
|
||||
<p>反射是由 reflect 包提供支持. 它定義了兩箇重要的類型, Type 和 Value. 一箇 Type 錶示一箇Go類型. 它是一箇接口, 有許多方法來區分類型和檢査它們的組件, 例如一箇結構體的成員或一箇函數的參數等. 唯一能反映 reflect.Type 實現的是接口的類型描述信息(§7.5), 衕樣的實體標識了動態類型的接口值.</p>
|
||||
<p>函數 reflect.TypeOf 接受任意的 interface{} 類型, 併返迴對應動態類型的reflect.Type:</p>
|
||||
<pre><code class="lang-Go">t := reflect.TypeOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Type</span>
|
||||
fmt.Println(t.String()) <span class="hljs-comment">// "int"</span>
|
||||
fmt.Println(t) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>其中 TypeOf(3) 調用將值 3 作爲 interface{} 類型參數傳入. 迴到 7.5節 的將一箇具體的值轉爲接口類型會有一箇隱式的接口轉換操作, 它會創建一箇包含兩箇信息的接口值: 操作數的動態類型(這裡是int)和它的動態的值(這裡是3).</p>
|
||||
<p>因爲 reflect.TypeOf 返迴的是一箇動態類型的接口值, 它總是返迴具體的類型. 因此, 下麫的代碼將打印 "*os.File" 而不是 "io.Writer". 稍後, 我們將看到 reflect.Type 是具有識別接口類型的錶達方式功能的.</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> w io.Writer = os.Stdout
|
||||
fmt.Println(reflect.TypeOf(w)) <span class="hljs-comment">// "*os.File"</span>
|
||||
</code></pre>
|
||||
<p>要註意的是 reflect.Type 接口是滿足 fmt.Stringer 接口的. 因爲打印動態類型值對於調試和日誌是有幫助的, fmt.Printf 提供了一箇簡短的 %T 標誌參數, 內部使用 reflect.TypeOf 的結果輸齣:</p>
|
||||
<pre><code class="lang-Go">fmt.Printf(<span class="hljs-string">"%T\n"</span>, <span class="hljs-number">3</span>) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>reflect 包中另一箇重要的類型是 Value. 一箇 reflect.Value 可以持有一箇任意類型的值. 函數 reflect.ValueOf 接受任意的 interface{} 類型, 併返迴對應動態類型的reflect.Value. 和 reflect.TypeOf 類似, reflect.ValueOf 返迴的結果也是對於具體的類型, 但是 reflect.Value 也可以持有一箇接口值.</p>
|
||||
<pre><code class="lang-Go">v := reflect.ValueOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Value</span>
|
||||
fmt.Println(v) <span class="hljs-comment">// "3"</span>
|
||||
fmt.Printf(<span class="hljs-string">"%v\n"</span>, v) <span class="hljs-comment">// "3"</span>
|
||||
fmt.Println(v.String()) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> "<int Value>"</span>
|
||||
</code></pre>
|
||||
<p>和 reflect.Type 類似, reflect.Value 也滿足 fmt.Stringer 接口, 但是除非 Value 持有的是字符串, 否則 String 隻是返迴具體的類型. 相衕, 使用 fmt 包的 %v 標誌參數, 將使用 reflect.Values 的結果格式化.</p>
|
||||
<p>調用 Value 的 Type 方法將返迴具體類型所對應的 reflect.Type:</p>
|
||||
<pre><code class="lang-Go">t := v.Type() <span class="hljs-comment">// a reflect.Type</span>
|
||||
fmt.Println(t.String()) <span class="hljs-comment">// "int"</span>
|
||||
</code></pre>
|
||||
<p>逆操作是調用 reflect.ValueOf 對應的 reflect.Value.Interface 方法. 它返迴一箇 interface{} 類型錶示 reflect.Value 對應類型的具體值:</p>
|
||||
<pre><code class="lang-Go">v := reflect.ValueOf(<span class="hljs-number">3</span>) <span class="hljs-comment">// a reflect.Value</span>
|
||||
x := v.Interface() <span class="hljs-comment">// an interface{}</span>
|
||||
i := x.(<span class="hljs-typename">int</span>) <span class="hljs-comment">// an int</span>
|
||||
fmt.Printf(<span class="hljs-string">"%d\n"</span>, i) <span class="hljs-comment">// "3"</span>
|
||||
</code></pre>
|
||||
<p>一箇 reflect.Value 和 interface{} 都能保存任意的值. 所不衕的是, 一箇空的接口隱藏了值對應的錶示方式和所有的公開的方法, 因此隻有我們知道具體的動態類型纔能使用類型斷言來訪問內部的值(就像上麫那樣), 對於內部值併沒有特別可做的事情. 相比之下, 一箇 Value 則有很多方法來檢査其內容, 無論它的具體類型是什麼. 讓我們再次嘗試實現我們的格式化函數 format.Any.</p>
|
||||
<p>我們使用 reflect.Value 的 Kind 方法來替代之前的類型 switch. 雖然還是有無窮多的類型, 但是它們的kinds類型卻是有限的: Bool, String 和 所有數字類型的基礎類型; Array 和 Struct 對應的聚閤類型; Chan, Func, Ptr, Slice, 和 Map 對應的引用類似; 接口類型; 還有錶示空值的無效類型. (空的 reflect.Value 對應 Invalid 無效類型.)</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch12/format
|
||||
<span class="hljs-keyword">package</span> format
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"reflect"</span>
|
||||
<span class="hljs-string">"strconv"</span>
|
||||
)
|
||||
|
||||
<span class="hljs-comment">// Any formats any value as a string.</span>
|
||||
<span class="hljs-keyword">func</span> Any(value <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">return</span> formatAtom(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
<span class="hljs-comment">// formatAtom formats a value without inspecting its internal structure.</span>
|
||||
<span class="hljs-keyword">func</span> formatAtom(v reflect.Value) <span class="hljs-typename">string</span> {
|
||||
<span class="hljs-keyword">switch</span> v.Kind() {
|
||||
<span class="hljs-keyword">case</span> reflect.Invalid:
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-string">"invalid"</span>
|
||||
<span class="hljs-keyword">case</span> reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
<span class="hljs-keyword">return</span> strconv.FormatInt(v.Int(), <span class="hljs-number">10</span>)
|
||||
<span class="hljs-keyword">case</span> reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
<span class="hljs-keyword">return</span> strconv.FormatUint(v.Uint(), <span class="hljs-number">10</span>)
|
||||
<span class="hljs-comment">// ...floating-point and complex cases omitted for brevity...</span>
|
||||
<span class="hljs-keyword">case</span> reflect.Bool:
|
||||
<span class="hljs-keyword">return</span> strconv.FormatBool(v.Bool())
|
||||
<span class="hljs-keyword">case</span> reflect.String:
|
||||
<span class="hljs-keyword">return</span> strconv.Quote(v.String())
|
||||
<span class="hljs-keyword">case</span> reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
<span class="hljs-keyword">return</span> v.Type().String() + <span class="hljs-string">" 0x"</span> +
|
||||
strconv.FormatUint(<span class="hljs-typename">uint64</span>(v.Pointer()), <span class="hljs-number">16</span>)
|
||||
<span class="hljs-keyword">default</span>: <span class="hljs-comment">// reflect.Array, reflect.Struct, reflect.Interface</span>
|
||||
<span class="hljs-keyword">return</span> v.Type().String() + <span class="hljs-string">" value"</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>到目前未知, 我們的函數將每箇值視作一箇不可分割沒有內部結構的, 因此它叫 formatAtom. 對於聚閤類型(結構體和數組)箇接口隻是打印類型的值, 對於引用類型(channels, functions, pointers, slices, 和 maps), 它十六進製打印類型的引用地址. 雖然還不夠理想, 但是依然是一箇重大的進步, 併且 Kind 隻關心底層錶示, format.Any 也支持新命名的類型. 例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> x <span class="hljs-typename">int64</span> = <span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">var</span> d time.Duration = <span class="hljs-number">1</span> * time.Nanosecond
|
||||
fmt.Println(format.Any(x)) <span class="hljs-comment">// "1"</span>
|
||||
fmt.Println(format.Any(d)) <span class="hljs-comment">// "1"</span>
|
||||
fmt.Println(format.Any([]<span class="hljs-typename">int64</span>{x})) <span class="hljs-comment">// "[]int64 0x8202b87b0"</span>
|
||||
fmt.Println(format.Any([]time.Duration{d})) <span class="hljs-comment">// "[]time.Duration 0x8202b87e0"</span>
|
||||
</code></pre>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S錶達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S錶達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S錶達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S錶達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Wed Dec 09 2015 15:54:13 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Mon Dec 14 2015 11:30:54 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -2060,7 +2060,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第十二章-反射">第十二章 反射</h1>
|
||||
<p>TODO</p>
|
||||
<p>Go提供了一種機製在運行時更新變量和檢査它們的值, 調用它們的方法, 和它們支持的內在操作, 但是在編譯時併不知道這些變量的類型. 這種機製被稱爲反射. 反射也可以讓我們將類型本身作爲第一類的值類型處理.</p>
|
||||
<p>在本章, 我們將探討Go語言的反射特性, 看看它可以給語言增加哪些錶達力, 以及在兩箇至關重要的API是如何用反射機製的: 一箇是 fmt 包提供的字符串格式功能, 另一箇是類似 encoding/json 和 encoding/xml 提供的鍼對特定協議的編解碼功能. 對於我們在4.6節中看到過的 text/template 和 html/template 包, 它們的實現也是依賴反射技術的. 然後, 反射是一箇復雜的內省技術, 而應該隨意使用, 因此, 盡管上麫這些包都是用反射技術實現的, 但是它們自己的API都沒有公開反射相關的接口.</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user