W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
原文鏈接:https://gopl-zh.github.io/ch12/ch12-02.html
反射是由 reflect 包提供的。它定義了兩個重要的類型,Type 和 Value。一個 Type 表示一個Go類型。它是一個接口,有許多方法來區(qū)分類型以及檢查它們的組成部分,例如一個結(jié)構(gòu)體的成員或一個函數(shù)的參數(shù)等。唯一能反映 reflect.Type 實現(xiàn)的是接口的類型描述信息(§7.5),也正是這個實體標識了接口值的動態(tài)類型。
函數(shù) reflect.TypeOf 接受任意的 interface{} 類型,并以 reflect.Type 形式返回其動態(tài)類型:
t := reflect.TypeOf(3) // a reflect.Type
fmt.Println(t.String()) // "int"
fmt.Println(t) // "int"
其中 TypeOf(3) 調(diào)用將值 3 傳給 interface{} 參數(shù)?;氐?7.5節(jié) 的將一個具體的值轉(zhuǎn)為接口類型會有一個隱式的接口轉(zhuǎn)換操作,它會創(chuàng)建一個包含兩個信息的接口值:操作數(shù)的動態(tài)類型(這里是 int)和它的動態(tài)的值(這里是 3)。
因為 reflect.TypeOf 返回的是一個動態(tài)類型的接口值,它總是返回具體的類型。因此,下面的代碼將打印 "*os.File" 而不是 "io.Writer"。稍后,我們將看到能夠表達接口類型的 reflect.Type。
var w io.Writer = os.Stdout
fmt.Println(reflect.TypeOf(w)) // "*os.File"
要注意的是 reflect.Type 接口是滿足 fmt.Stringer 接口的。因為打印一個接口的動態(tài)類型對于調(diào)試和日志是有幫助的, fmt.Printf 提供了一個縮寫 %T 參數(shù),內(nèi)部使用 reflect.TypeOf 來輸出:
fmt.Printf("%T\n", 3) // "int"
reflect 包中另一個重要的類型是 Value。一個 reflect.Value 可以裝載任意類型的值。函數(shù) reflect.ValueOf 接受任意的 interface{} 類型,并返回一個裝載著其動態(tài)值的 reflect.Value。和 reflect.TypeOf 類似,reflect.ValueOf 返回的結(jié)果也是具體的類型,但是 reflect.Value 也可以持有一個接口值。
v := reflect.ValueOf(3) // a reflect.Value
fmt.Println(v) // "3"
fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // NOTE: "<int Value>"
和 reflect.Type 類似,reflect.Value 也滿足 fmt.Stringer 接口,但是除非 Value 持有的是字符串,否則 String 方法只返回其類型。而使用 fmt 包的 %v 標志參數(shù)會對 reflect.Values 特殊處理。
對 Value 調(diào)用 Type 方法將返回具體類型所對應的 reflect.Type:
t := v.Type() // a reflect.Type
fmt.Println(t.String()) // "int"
reflect.ValueOf 的逆操作是 reflect.Value.Interface 方法。它返回一個 interface{} 類型,裝載著與 reflect.Value 相同的具體值:
v := reflect.ValueOf(3) // a reflect.Value
x := v.Interface() // an interface{}
i := x.(int) // an int
fmt.Printf("%d\n", i) // "3"
reflect.Value 和 interface{} 都能裝載任意的值。所不同的是,一個空的接口隱藏了值內(nèi)部的表示方式和所有方法,因此只有我們知道具體的動態(tài)類型才能使用類型斷言來訪問內(nèi)部的值(就像上面那樣),內(nèi)部值我們沒法訪問。相比之下,一個 Value 則有很多方法來檢查其內(nèi)容,無論它的具體類型是什么。讓我們再次嘗試實現(xiàn)我們的格式化函數(shù) format.Any。
我們使用 reflect.Value 的 Kind 方法來替代之前的類型 switch。雖然還是有無窮多的類型,但是它們的 kinds 類型卻是有限的:Bool、String 和 所有數(shù)字類型的基礎(chǔ)類型;Array 和 Struct 對應的聚合類型;Chan、Func、Ptr、Slice 和 Map 對應的引用類型;interface 類型;還有表示空值的 Invalid 類型。(空的 reflect.Value 的 kind 即為 Invalid。)
gopl.io/ch12/format
package format
import (
"reflect"
"strconv"
)
// 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"
}
}
到目前為止,我們的函數(shù)將每個值視作一個不可分割沒有內(nèi)部結(jié)構(gòu)的物品,因此它叫 formatAtom。對于聚合類型(結(jié)構(gòu)體和數(shù)組)和接口,只是打印值的類型,對于引用類型(channels、functions、pointers、slices 和 maps),打印類型和十六進制的引用地址。雖然還不夠理想,但是依然是一個重大的進步,并且 Kind 只關(guān)心底層表示,format.Any 也支持具名類型。例如:
var x int64 = 1
var d time.Duration = 1 * time.Nanosecond
fmt.Println(format.Any(x)) // "1"
fmt.Println(format.Any(d)) // "1"
fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0"
fmt.Println(format.Any([]time.Duration6116161)) // "[]time.Duration 0x8202b87e0"
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: