如何将 interface{} 转换为其底层类型?

huangapple go评论99阅读模式
英文:

how to convert a interface{} to its underlying type

问题

我在playground中有一些代码:示例代码

我将一个二维字符串切片传递给一个名为test的函数,该函数可以接受可变参数。在test()函数中,我可以获取第一个参数的底层类型,但是如何将其转换回其底层类型呢?因为我需要对其底层类型进行迭代。

我不想像这样硬编码:

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

问题是,如果我知道它的类型字符串是"[][]string"或其他什么,我该如何将其转换为该类型?

我在这里发布了完整的代码,并添加了一些注释:

package main

import (
    "reflect"
    "fmt"
)

func test(args ...interface{}) {
    fmt.Println("type", reflect.TypeOf(args[0]))
    // 这里会有一个编译错误,因为args[0]的类型现在是interface{},而不是切片,尽管它的底层类型是切片

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

    // 那么,当我获取到类型字符串"[][]string"时,如何将args[0]转换为[][]string呢?
    // 我知道可以使用类型断言,但是你必须猜测args[0]可能的类型。
    // 有没有一种方法可以从类型的字符串表示转换为实际类型?
    // 这样我就可以编写这样的代码:
    // val := args[0].(reflect.TypeOf(args[0]).String()),这样非常通用

}

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

希望对你有所帮助!

英文:

I have some code in the playground: sample code

I pass a two dimension string slice into a function test, which can accept variadic arguments, and in test() I can get the first argument's underlying type, but how can I convert it back to its underlying type? because I have to iterate on its underlying type

I don't like to hard code it like:

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

the question is if I know its type string is "[][]string" or something else, how can I convert it to the type?

I post the full code here, and add some comments:

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)
}

答案1

得分: 4

另一种对ANisus答案的变体:

package main

import "fmt"

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

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

另外,可以使用reflect包:

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

英文:

Another variation on ANisus answer :

package main

import &quot;fmt&quot;

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

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

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


Otherwise, use the reflect package :

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

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

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

func main() {
var x = [][]string{{&quot;Hello&quot;}, {&quot;World&quot;, &quot;!&quot;}}
var y = []int{2,3,5,7,9}
var z = &#39;H&#39;

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

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

答案2

得分: 3

我不完全确定你想要实现什么。

如果你将一个[][]string作为第一个参数传入,为了遍历它,你必须进行类型断言类型切换

switch val := args[0].(type) {
case [][]string:
    // 在这里处理类型为[][]string的val
}

你可以在Go规范中了解更多关于类型切换的内容:http://golang.org/ref/spec#Switch_statements

如果你尝试将[][]string切片不变地传递给test函数:

test(arr...) // 尝试不变地传递切片。

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

> 如果最后一个参数可以赋值给切片类型[]T,并且在参数后面跟着...,那么它可以不变地作为...T参数的值传递,此时不会创建新的切片。

英文:

I am not entirely sure what you wish to achieve.

If you pass in a [][]string as the first argument, in order to iterate over it, you must do a type assertion or type switch:

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

You can read more about type switches in the Go specifications: http://golang.org/ref/spec#Switch_statements

If you try to pass the [][]string slice unchanged to test:

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

this will fail. This is due to [][]string not being assignable to []interface{}. The Go specification states:

> If the final argument is assignable to a slice type []T, it may be
> passed unchanged as the value for a ...T parameter if the argument is
> followed by .... In this case no new slice is created.

答案3

得分: 1

使用动态值进行类型断言是不可能的,因此 foo.(reflect.TypeOf(bar)) 或类似的写法是行不通的。这是因为类型不是一等公民,不能存储在变量中。

你必须明确地写出你希望接口值是哪种类型,要么通过使用类型断言,要么通过使用类型切换。

英文:

It is not possible to use a dynamic value for a type assertion, so

foo.(reflect.TypeOf(bar))

or something similar will not work. This is because types are not first class citizens
and can't be stored in a variable.

You always have to write explictly which type you want the interface value to be, either
by using a type assertion or a type switch.

huangapple
  • 本文由 发表于 2014年1月23日 21:38:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/21309784.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定