前言:
平常在臉書社群或是 Slack channel (https://t.me/golangtw) 都會聽到一些常問的問題,決定把它整理一下,變成這篇文章,也希望讓更多人經過搜尋來了解與得到解答。 由於經常詢問的問題還不少,這算是一個系列的整理,希望能透過整理成文章的方式給予大家比較深入的了解。
第一篇首先是 Type Assertion ,希望大家在型別轉換上能夠更了解。
相關系列文章整理:
Type Assertion:
範例程式碼: https://play.golang.org/p/Ya8tVYmdpko
不好意思
想請問這一段
resp, ok := datum.(*module.Response)
那個ok是代表甚麼意思啊
是代表datum這個interface可以成功轉成Request 這個struct的意思嗎?
這種用法叫做 Type Assertion (Go Tour 有相關說明),主要是用來做轉型判斷。透過 Type Assertion 主要的好處可以確認是否可以正確的轉型成功。
當然 reflect
也可以轉換數值,但是比較適合使用在當你拿到一個參數卻不知道當初是使用哪一種資料型態。才透過 reflect
套件來取得型態與取得數值內容。
簡單一點的例子而言:
func parseInterfceToString(in interface{}) string {
if val, ok := in.(string); ok {
return val
}
return ""
}
上面的例子,因為已經確定只支援 string
的解析方式,所以可以直接使用 type assertion
的方式來轉址。 但是很多時候,如果希望能夠支援多種型態,並且能夠正確的讀取數值的話,又該如何做呢?
func createQuery(q interface{}) {
t := reflect.TypeOf(q)
v := reflect.ValueOf(q)
fmt.Println("Type ", t)
fmt.Println("Value ", v)
}
這是一個相當簡單的 reflect
範例,可以讓妳得知輸入進來的資料型別與實際資料。 直接透過 reflect.Type.Kind()
來取得型別做處理的範例可以參考以下:
func examiner(t reflect.Type, depth int) {
fmt.Println(strings.Repeat("\t", depth), "Type is", t.Name(), "and kind is", t.Kind())
switch t.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice:
fmt.Println(strings.Repeat("\t", depth+1), "Contained type:")
examiner(t.Elem(), depth+1)
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Println(strings.Repeat("\t", depth+1), "Field", i+1, "name is", f.Name, "type is", f.Type.Name(), "and kind is", f.Type.Kind())
if f.Tag != "" {
fmt.Println(strings.Repeat("\t", depth+2), "Tag is", f.Tag)
fmt.Println(strings.Repeat("\t", depth+2), "tag1 is", f.Tag.Get("tag1"), "tag2 is", f.Tag.Get("tag2"))
}
}
}
}
這個範例,根據不同型別 (array, channel, map ) 則繼續往裡面去解析,如果是 struct 就印出相關結構。
但是值得注意的是,如果需要真正把資料型態轉成該型態的話。還是得透過 type assertion 比較安全。不然透過 reflect.Value
取值得方式來做轉換,預設都是會 panic 。
結論:
如果只支援某些型態的處理,在轉換型別的處理上建議直接使用 type assertion
就可以輕鬆完成。如果想要處理更多型態的方式(slice, struct) 的處理跟轉型,可能就得透過 reflect
來慢慢處理。
也希望整理的相關資料能夠幫助大家。