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

ゴーラこれはコストがかかり、構文上のコストのかかるプロセスを隠しているため、 '[]]文を' []インタフェース{} 'に暗黙的にキャストしてはいけません。 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 
} 

これで、空のインターフェイス{}が明確になります。

インタフェース{...}は宣言されたメソッドが異なり、メンバを持つことができません(構造体のように)。メソッドのみ、およびその基礎となる型はインタフェースで宣言されたすべてのメソッドを実装する必要があります。あなたがあればインターフェイスのすべてのメソッドを実装する型は、その後、そのインターフェイス型にキャストすることができ

type actionPerformer interface { 
    action(interface{}) 
} 

(彼らは何かをやっているとのインターフェース名は接尾辞「ER」を持っている必要があります)インターフェースactionPerformerを持つことができますインタフェース上でこれらのメソッドの1つを呼び出すと、基本となる型のメソッドが実行されます。 たとえば、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機能は、鋳造そのインターフェイスは、すべてのよりもはるかに効率的である

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

またかもしれません(golangでは、名前には、アンダースコアを使用していない、キャメルケース形式にする必要があります)タイプ文のリストを持っており、それにメソッドを追加することが有用である。このようなもののこつを取得

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{}を使用できます。その後、必要に応じてキャストします。タイプ[]Statement

func performAction(action interface{}, v interface{}) { 
    for _, each := range v.([]Statement) { 
     action.(func(Statement))(each) 
    } 
} 
  • データは、タイプfunc(Statement)のためにも[]interface{}
  • に割り当てることはできませんその後、元の型にキャストし、使用interface{}func(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

素晴らしい提案ですが、私はキャスティングを必要としないものを期待していました。 –

関連する問題