在我的实际代码中,我使用encoding/xml
解析XML文档,我基本上有一堆以下形式的嵌套结构 - 所有这些都可能多次出现,除了顶部 - 级别statements
元素:
statements
statement
opcode
args
pre
post
我是Go的新手,我明显误解了interface{}
(空接口)的工作方式:
.\stmtgen.go:58: cannot use print_name (type func(Statement)) as type func(interface {}) in argument to performAction
.\stmtgen.go:58: cannot use slist (type []Statement) as type []interface {} in argument to performAction
相关示例代码:
package main
import "fmt"
// Actually a structure in my code, but this suffices for demonstration.
type Opcode int
// A Statement has a Name and multiple Opcodes may use this Name.
type Statement struct {
Name string
Opcodes []Opcode
}
// Print the statement name.
func print_name(stmt Statement) {
fmt.Println(stmt.Name)
}
// Perform an action on each item of a collection.
func performAction(action func(interface{}), v []interface{}) {
for i := range v {
action(v[i])
}
}
func main() {
slist := make([]Statement, 3)
slist[0] = Statement{"Statement 1"}
slist[1] = Statement{"Statement 2"}
slist[2] = Statement{"Statement 3"}
//ERROR HERE
performAction(print_name, slist)
}
我必须创建函数来打印每种类型的值吗?
答案 0 :(得分:1)
空interface{}
可以包含任何值,并作为类型interface{}
传递。当你需要它的值时,你可以执行这样的类型断言:
var anyValue interface{}
anyValue = "hello"
strValue := anyValue.(string)
如果anyValue不属于被断言的类型,则会引起恐慌
如果接口属于具有多次返回的类型,则类型断言也可用于返回bool
strValue, ok := anyValue.(string)
if ok {
//anyValue contains a string!
}
如果您不知道界面的类型,可以使用开关确定它的类型如下:
switch val := anyValue.(type) {
case string:
// anyValue contains a string
// and val is a string
break
case int:
// anyValue contains an int
// and val is and int
break
default:
//unhandled interface type
}
希望这会使空接口{}类型更清晰。
接口{...}具有在其中声明的方法是不同的,它们不能有成员(如结构可以),只有方法,它们的基础类型必须实现接口中声明的所有方法。你可以有一个接口actionPerformer(接口名称应该有后缀“呃”,因为他们正在做的事情)
type actionPerformer interface {
action(interface{})
}
实现接口中所有方法的类型可以转换为该接口类型,然后如果在接口上调用其中一个方法,它将在基础类型上运行该方法。
例如,如果Statement结构实现action(interface{})
方法,则可以将Statement结构强制转换为actionPerformer
类型,如果action(interface{})
上调用actionPerformer
函数,则运行Statement结构上的动作函数。因此,您可以拥有多个类型,这些类型都具有action(interface{})
方法,并且可以将它们全部转换为actionPerformer
,您可以将其称为操作函数。
func (code Opcode) action(arg interface{}) {
fmt.Println(arg.(int) + int(code))
}
func (stmt Statement) action(arg interface{}) {
fmt.Println(arg.(string), stmt.Name)
}
stmt := Statement{"Statement 1", nil}
stmtActionPerformer := actionPerformer(stmt)
opcode := Opcode(5)
opcodeActionPerformer := actionPerformer(opcode)
stmtActionPerformer.action("hello") // will print "hello "+whatever the statements name is
opcodeActionPerformer.action(2) //will print be 7
类型断言仍可用于这些类型的接口,例如
stmt := stmtActionPerformer.(Statement)
fmt.Println(stmt.Name)
这是一个人为的例子,但考虑到这一点,您可能希望使用这样的接口编写代码。 请记住,界面之间的拼接是昂贵的,所以应该谨慎使用,但如果使用正确,它们是一个强大的工具。
对于您的示例,一个简单的printNames函数将比所有接口转换更有效(请注意,在golang中,名称应采用CamelCase格式,而不是使用下划线)
func printNames(stmts []Statement) {
for _, stmt := range stmts {
fmt.Println(stmt.Name)
}
}
使用StatementList类型并向其添加方法可能也很有用:
type StatementList []Statement
func (list StatementList) printNames() {
for _, stmt := range list {
fmt.Println(stmt.Name)
}
}
掌握这些东西让golang变得更有趣,希望这有助于:)
答案 1 :(得分:0)
您必须声明performAction
的参数与参数类型完全相同。
func performAction(action func(Statement), v []Statement) {
for i := range v {
action(v[i])
}
}
或者您可以在所有参数上使用interface{}
。然后根据需要进行投射。
func performAction(action interface{}, v interface{}) {
for _, each := range v.([]Statement) {
action.(func(Statement))(each)
}
}
[]Statement
类型的数据分配给[]interface{}
func(Statement)
也无法分配到func(interface{})
使用interface{}
,然后将其转换为原始类型。
答案 2 :(得分:0)
这对我有用:
package main
import (
"fmt"
)
// Actually a structure in my code, but this suffices for demonstration.
type Opcode int
// A Statement has a Name and multiple Opcodes may use this Name.
type Statement struct {
Name string
Opcodes []Opcode
}
// Print the statement name.
func print_name(stmt interface{}) {
if s, ok := stmt.(Statement); !ok {
panic("typ err")
} else {
fmt.Println(s.Name)
}
}
// Perform an action on each item of a collection.
func performAction(action func(interface{}), v []interface{}) {
for i := range v {
action(v[i])
}
}
func main() {
slist := make([]interface{}, 3)
slist[0] = Statement{"Statement 1", nil}
slist[1] = Statement{"Statement 2", nil}
slist[2] = Statement{"Statement 3", nil}
performAction(print_name, slist)
/*output:
Statement 1
Statement 2
Statement 3
*/
}