【问题标题】:how to convert a interface{} to its underlying type如何将 interface{} 转换为其底层类型
【发布时间】:2014-02-14 02:18:26
【问题描述】:

我在操场上有一些代码:sample code

我将一个二维字符串切片传递给一个函数 test,它可以接受可变参数,并且在 test() 中我可以获得第一个参数的基础类型,但是如何将它转换回它的基础类型?因为我必须迭代它的底层类型

我不喜欢这样硬编码:

if reflect.TypeOf(args[0]).String() == "[][]string" {
    val := args[0].([][]string)
}

问题是如果我知道它的类型字符串是“[][]string”或别的什么,我怎样才能把它转换成类型?

我把完整的代码贴在这里,并添加一些cmets:

package main

import (
    "reflect"
    "fmt"
)

func test(args ...interface{}) {
    fmt.Println("type", reflect.TypeOf(args[0]))
    // here will be a compile error, because args[0]'type now is interface{}, 
    // not a slice, though it's underlying type is slice

    for i, v := range args[0] {    

    }

    // so, how could I convert args[0] to [][]string when I get its type 
    // string "[][]string" ?
    // I know use type assertion is possible, but you must guess the possible 
    // type the args[0] will be.
    // is there a way to do it from a type's string representation to the 
    // actual type?
    // so I can write code like this: 
    // val := args[0].(reflect.TypeOf(args[0]).String()), which is very general


}
func main() {
    arr := [][]string{{"asd", "sd", "rt"}, {"34","gf","gf"}}
    test(arr)
}

【问题讨论】:

  • 这看起来像一个“A B 问题”。你能解释一下你打算用你的代码做什么吗?
  • 你确定你真的需要反射并且无法摆脱类型断言/切换吗?

标签: reflection interface go slice


【解决方案1】:

ANisus 答案的另一个变体:

package main

import "fmt"

func main() {
    var x interface{} = [][]string{{"Hello"}, {"World", "!"}}

    if y, ok := x.([][]string); ok {
        fmt.Printf("%v", y)
    }
}

http://play.golang.org/p/tGYbhzuUnr


否则,使用反射包:

import (
    "fmt"
    "reflect"
)

func process(i interface{}) {
    fmt.Printf("Processing %v\n", i)

    if reflect.TypeOf(i).Kind() == reflect.Slice {
        v := reflect.ValueOf(i)
        for i := 0; i < v.Len(); i++ {
            process(v.Index(i).Interface())
        }
    }
}

func main() {
var x = [][]string{{"Hello"}, {"World", "!"}}
var y = []int{2,3,5,7,9}
var z = 'H'

process(x)
process(y)
process(z)
}

http://play.golang.org/p/MAqIgxzLuC

【讨论】:

  • 问题仍然存在:你想用val做什么?
  • 我想使用一个 for 循环来遍历它的元素,这样我就可以处理它们中的每一个
  • 使用reflect
【解决方案2】:

我不完全确定您希望达到什么目标。

如果您传入[][]string 作为第一个参数,为了对其进行迭代,您必须执行type assertiontype switch

switch val := args[0].(type) {
case [][]string:
    // Do what you want with val which is of type [][]string
}

您可以在 Go 规范中阅读有关类型开关的更多信息:http://golang.org/ref/spec#Switch_statements

如果您尝试将 [][] 字符串切片原封不动地传递给test

test(arr...) // Attempt to  pass the array unchanged.

这将失败。这是因为 [][]string 不能分配给 []interface{}。 Go 规范指出:

如果最后一个参数可分配给切片类型 []T,它可能是 如果参数是,则作为 ...T 参数的值原封不动地传递 后跟 ...。在这种情况下,不会创建新切片。

【讨论】:

  • 感谢您的回答。我知道我可以通过类型断言来转换它,但是当这样做时,你必须编写switch case,对吗?你必须猜测你会遇到什么情况。所以我想要的是,如果我知道类型字符串,例如“[][]string”,我可以将 interface{} 转换为 [][]string
  • 不知道我把问题说清楚了,你可以认为我想从它的字符串表示“[][]string”中获取类型[][]string
  • @zhaozhi 恐怕不是很明白。是否要将 [][]string 转换为 []interface{} 以便遍历每个 []string?您可以使用反射来查看 interface{} 是否包含切片,然后对其进行迭代,而无需使用类型断言。
  • 在函数 test() 中,我想将 args[0] 从它的类型(接口{})转换为它的底层类型([][]string)。一旦你得到它的类型字符串,即“[][]string”,你能在不使用类型断言的情况下完成这个转换吗?
  • @zhaozhi 不行,你需要做一个类型断言才能得到一个具体的类型。
【解决方案3】:

类型断言不能使用动态值,所以

foo.(reflect.TypeOf(bar))

或类似的东西不起作用。这是因为类型不是一等公民 并且不能存储在变量中。

你总是必须明确地写出你想要接口值的类型,要么 通过使用类型断言或类型开关。

【讨论】:

    猜你喜欢
    • 2017-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-11
    • 2011-10-28
    • 2014-06-16
    • 1970-01-01
    • 2016-10-15
    相关资源
    最近更新 更多