Go開發(fā)的各種坑 - 類型判斷

1. 概述

Golang中,關于類型的判斷,有幾種方法

前兩種方法的原理一致,實現(xiàn)的功能也相似,都不能解決類型別名的問題,因為類型別名是一種新的數據類型。
如果要解決類型別名的問題,需要通過reflect做類型判斷。
本文通過一些代碼來測試和驗證。

2. 類型選擇

在進行類型選擇類型斷言時,我們要很清楚我們要處理的數據的類型,只有這樣才能得到數據的值。
注意:類型別名,是一種新的類型。

package main

import "fmt"

type myStr string
type aliasSlice0 []any
type aliasSlice1 []any

func test00(data any) {
    switch value := data.(type) {
    case []any:
        fmt.Println("type: []any", value)
    case []string:
        fmt.Println("type: []string", value)
    case aliasSlice0:
        fmt.Println("type: aliasSlice0", value)
    case myStr:
        fmt.Println("type: myStr", value)
    case string:
        fmt.Println("type: string", value)
    default:
        fmt.Println("default", value)
    }
}

func main() {
    test00(myStr("ddd"))
    test00("xxx")
    test00([]any{"aaa", "ccc"})
    test00([]string{"aaa", "ccc"})
    test00(aliasSlice0{"aaa", "ccc"})
    test00(aliasSlice1{"aaa", "ccc"})
}

  • 運行結果
type: myStr ddd
type: string xxx
type: []any [aaa ccc]
type: []string [aaa ccc]
type: aliasSlice0 [aaa ccc]
default [aaa ccc]

3. Reflect反射

通過反射,可以解決類型別名的問題,也可以很靈活地完成數據的處理。
只是操作比較麻煩,且處理時要考慮各種異常情況,不然,很容易出現(xiàn)bug。
有時間,可以基于reflect做一個package,做些事情,還是挺有意思的。

package main

import (
    "fmt"
    "reflect"
)

type aliasStr string

type skyObject interface {
    fly()
}

type Bird struct {
    name string
}

func (b *Bird) fly() {
    fmt.Println(b.name + "is flying")
}

func test01(data any) {
    typ := reflect.TypeOf(data).Kind()

    switch typ {
    case reflect.Map:
        iterMap := reflect.ValueOf(data).MapRange()
        for iterMap.Next() {
            if iterMap.Key().Kind() != reflect.String {
                fmt.Println("error ... key type != string")
                continue
            }
            switch iterMap.Value().Kind() {
            case reflect.Interface:
                if iterMap.Value().NumMethod() == 0 {
                    fmt.Println("map[string]any", data)
                } else {
                    fmt.Println("map[string]interface{...}", data)
                }
            case reflect.String:
                fmt.Println("map[string]string", data)
            case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                fmt.Println("map[string]int", data)
            case reflect.Bool:
                fmt.Println("map[string]bool", data)
            case reflect.Slice, reflect.Array:
            case reflect.Struct:
            case reflect.Map:
            default:
                fmt.Println("default", data)
            }
        }
    case reflect.String:
        fmt.Println("Type: string", data)
    }
}

func main() {
    fmt.Println("=== Test === string")
    test01(aliasStr("aaa"))
    test01("bbb")

    fmt.Println("=== Test === map[string]string")
    test01(map[string]string{"aaa": "a1", "ccc": "c1"})

    fmt.Println("=== Test === map[string]any")
    test01(map[string]any{"aaa": "a1", "ccc": "c1"})

    fmt.Println("=== Test === map[string]skyObject")
    data := map[string]skyObject{
        "aaa": &Bird{name: "yanzi"},
        "ccc": &Bird{name: "laoying"},
    }
    test01(data)
}
  • 運行結果
=== Test === string
Type: string aaa
Type: string bbb
=== Test === map[string]string
map[string]string map[aaa:a1 ccc:c1]
map[string]string map[aaa:a1 ccc:c1]
=== Test === map[string]any
map[string]any map[aaa:a1 ccc:c1]
map[string]any map[aaa:a1 ccc:c1]
=== Test === map[string]skyObject
map[string]interface{...} map[aaa:0x1400010c1c0 ccc:0x1400010c1d0]
map[string]interface{...} map[aaa:0x1400010c1c0 ccc:0x1400010c1d0]

4. 后記

上述問題,是我在嘗試遍歷一個any類型的數據時遇到的。當然,該數據有一些限制,比如

  • map的key是string類型,value不限制類型
  • 數據中不存在struct、chan、pointer等特殊類型
  • 數據中可能存在slice

通過Type Switches方式遍歷時,發(fā)現(xiàn)居然存在primitive.A類型,這是mongo driver的一個類型。

// From: go.mongodb.org/mongo-driver/bson/primitive
type A []interface{}

并且,我通過[]any沒辦法case到,所以深入分析了一下原因,并嘗試通過reflect一勞永逸。
不過,最后我還是采用Type Switches方法完成整個數據的遍歷,因為reflect太麻煩。

另外,Switche-Case的語法,如果case多個類型時,value還會處理成any類型。例如下面的代碼,會報錯cannot use value (variable of type any) as string value in argument to p: need type assertion。

func main() {
    type myStr string

    var p = func(s string) {
        print(s)
    }

    var data any
    data = "hello world"
    switch value := data.(type) {
    case myStr, string:
        p(value)
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容