2016-04-27 58 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

偉大的建議,但我希望有東西,不需要鑄造。 –