2016-04-27 55 views
0

在我的实际代码中,我使用encoding/xml来解析XML文档,而且我基本上有一堆嵌套结构,其形式为—,所有这些可能会多次出现,除了顶级statements元素:对项目集合执行操作

statements 
    statement 
    opcode 
     args 
     pre 
     post 

我是相当新的去吧,我清楚地误解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

戈拉ng不会隐式地将'[] Statement'转换为'[] interface {}',因为这是昂贵的,并且隐藏昂贵的语法流程很糟糕,您必须将每个'Statement'转换为'interface {} for循环前传递给performAction(...) – nevernew

回答

1

interface{}可以包含通过周围的类型interface{}任何价值。当你需要从它的价值,你可以像这样进行类型断言:

var anyValue interface{} 
anyValue = "hello" 

strValue := anyValue.(string) 

如果anyValue是被认定的类型,那么它会引起恐慌

也可以使用的类型断言不返回一个布尔如果接口是该类型的具有多个返回

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类型,并且如果在actionPerformer上调用action(interface{})函数,则会运行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,名称应在驼峰格式,而不是使用下划线)

func printNames(stmts []Statement) { 
    for _, stmt := range stmts { 
     fmt.Println(stmt.Name) 
    } 
} 

那也说不定是有用的一类语句列表和添加方法是:

type StatementList []Statement 

func (list StatementList) printNames() { 
    for _, stmt := range list { 
     fmt.Println(stmt.Name) 
    } 
} 

获取这些东西的窍门使golang更多的乐趣,希望这有助于:)

+0

综合答案!谢谢! –

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{},然后将其转换为原始类型。

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 
    */ 
} 
+0

伟大的建议,但我希望有东西,不需要铸造。 –