对项目集合执行操作

时间:2016-04-27 04:29:50

标签: go

在我的实际代码中,我使用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)
}

我必须创建函数来打印每种类型的值吗?

3 个答案:

答案 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
    */
}