【问题标题】:How to check if interface{} is a slice如何检查 interface{} 是否为切片
【发布时间】:2017-03-13 14:41:50
【问题描述】:

我是围棋的菜鸟 :) 所以我的问题可能很愚蠢,但找不到答案,所以。

我需要一个函数:

func name (v interface{}) {
    if is_slice() {
        for _, i := range v {
            my_var := i.(MyInterface)
            ... do smth
        }
    } else {
        my_var := v.(MyInterface)
        ... do smth
    }
}

我如何在 Go 中使用 is_slice?感谢任何帮助。

【问题讨论】:

  • 其实我的问题是 v 是指针切片。经过一番调查,我认为首先迭代集合更好,至少我见过的实现使用了这种方法。一些复制/粘贴样板代码,但我认为现在不会有问题

标签: go interface type-conversion slice


【解决方案1】:

在您的情况下,type switch 是最简单、最方便的解决方案:

func name(v interface{}) {
    switch x := v.(type) {
    case []MyInterface:
        fmt.Println("[]MyInterface, len:", len(x))
        for _, i := range x {
            fmt.Println(i)
        }
    case MyInterface:
        fmt.Println("MyInterface:", x)
    default:
        fmt.Printf("Unsupported type: %T\n", x)
    }
}

case 分支枚举了可能的类型,在它们内部,x 变量已经属于该类型,因此您可以使用它。

测试它:

type MyInterface interface {
    io.Writer
}

var i MyInterface = os.Stdout
name(i)
var s = []MyInterface{i, i}
name(s)
name("something else")

输出(在Go Playground上试试):

MyInterface: &{0x1040e110}
[]MyInterface, len: 2
&{0x1040e110}
&{0x1040e110}
Unsupported type: string

对于单一类型检查,您也可以使用type assertion

if x, ok := v.([]MyInterface); ok {
    // x is of type []MyInterface
    for _, i := range x {
        fmt.Println(i)
    }
} else {
    // x is not of type []MyInterface or it is nil
}

还有其他方法,使用包 reflect 可以编写更通用(且速度更慢)的解决方案,但如果您刚刚开始 Go,则不应该深入研究反射。

【讨论】:

    【解决方案2】:

    is_slice 方法可以是这样的:

    func IsSlice(v interface{}) bool {
        return reflect.TypeOf(v).Kind() == reflect.Slice
    }
    

    如果需要,还可以添加reflect.TypeOf(v).Kind() == reflect.Array的附加条件。

    【讨论】:

    • 这是问题的实际答案。谢谢!
    【解决方案3】:

    icza's answer是正确的,但go creators不推荐:

    界面{}什么也没说

    更好的方法可能是为您拥有的每种类型定义一个函数:

    func name(v MyInterface) {
        // do something
    }
    
    func names(vs []MyInterface) {
        for _, v := range(vs) {
            name(v)
        }
    }
    

    【讨论】:

    • 想知道为什么 fmt 包的创建者没有为每种类型创建打印功能?
    • 感谢您提供有趣的链接。
    • @Alexander 你有一个“为每种类型打印”实现为打印动词:%s 用于字符串,%p 用于指针,%d 用于基数为 10 的整数等,您可以使用%s 并实现你自己的 String() 字符串函数来打印你自己的类型
    • 请注意,在这种情况下,您将无法直接向函数传递值切片,因为接口切片不是接口:github.com/golang/go/wiki/InterfaceSlice
    【解决方案4】:

    来自https://blog.golang.org/json

    解码任意数据

    考虑这个 JSON 数据,存储在变量 b 中:

    b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
    

    在不知道该数据结构的情况下,我们可以使用 Unmarshal 将其解码为 interface{} 值:

    var f 接口{} 错误 := json.Unmarshal(b, &f) 此时 f 中的 Go 值将是一个映射,其键是字符串,其值本身存储为空接口值:

    f = map[string]interface{}{
        "Name": "Wednesday",
        "Age":  6,
        "Parents": []interface{}{
            "Gomez",
            "Morticia",
        },
    }
    

    要访问这些数据,我们可以使用类型断言来访问 f 的底层 map[string]interface{}:

    m := f.(map[string]interface{})
    

    然后我们可以使用范围语句遍历映射,并使用类型开关来访问其值作为它们的具体类型:

    for k, v := range m {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "is string", vv)
        case float64:
            fmt.Println(k, "is float64", vv)
        case []interface{}:
            fmt.Println(k, "is an array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "is of a type I don't know how to handle")
        }
    }
    

    通过这种方式,您可以处理未知的 JSON 数据,同时仍然享受类型安全的好处。

    【讨论】:

      猜你喜欢
      • 2017-11-27
      • 2018-12-18
      • 2021-12-09
      • 1970-01-01
      • 2016-11-03
      • 1970-01-01
      • 1970-01-01
      • 2011-03-19
      相关资源
      最近更新 更多