3

簡単なバージョン
文字列の値に基づいて特定の型の変数を作成するにはどうすればよいでしょうか?

type ta struct { a int }
type tb struct { b float }
type tc struct { c string }

t := "tb"
v := MagicVarFunc(t) // Returns a new allocated var of type interface{}
v.(tb).b = 8.3

本当の例以下の驚くべきことに実際に動作する例では、 string
に基づいて動的に変数を作成しています。これは、文字列がキーで、の nil ポインターが値であるマップに各構造体型を登録することによって行われます。 各タイプは、その特定のタイプの新しい変数を返すメソッド New() とのインターフェースを実装します。

以下の例は、私がやりたいことに非常に近いものです。各アクションには、対応する構造体にデータを入力する一連の JSON エンコード データがあります。私がそれを構築した方法は、マップに登録する新しいスタンドアロン アクションを作成できるようにしたいからでもあります。

現在、言語を乱用しているかどうかはわかりません。
私が完全に気が狂っている場合、誰かが私に何か指針を与えることができますか? 明らかに簡単な方法はありますか?

package main

import (
    "fmt"
    "encoding/json"
)

// All I require of an action is that it may be executed
type ActionHandler interface {
    Exec()
    New() ActionHandler
}

// My list of actions
var mActions = make(map[string]ActionHandler)

// Action Exit (leaving the program)
type aExit struct {}
func (s *aExit) Exec() { fmt.Println("Good bye") }
func (s *aExit) New() ActionHandler { return new(aExit) }
func init() {
    var a *aExit
    mActions[`exit`] = a
}

// Action Say (say a message to someone)
type aSay struct {
    To  string
    Msg string
}
func (s *aSay) Exec() { fmt.Println(`You say, "` + s.Msg + `" to ` + s.To) }
func (s *aSay) New() ActionHandler { return new(aSay) }
func init() {
    var a *aSay
    mActions[`say`] = a
}

func inHandler(action string, data []byte) {
    a := mActions[action].New()
    json.Unmarshal(data, &a)
    a.Exec()
}

func main(){
    inHandler(`say`, []byte(`{"to":"Sonia","msg":"Please help me!"}`))
    inHandler(`exit`, []byte(`{}`))
}
4

3 に答える 3

7

実行時に値を取得できる場合は、リフレクションを使用してゼロ値を取得するか、リフレクションを使用して型の新しい値 ( などnew)を割り当てることができTypeます。ただし、文字列から を取得する方法はないと思いTypeます。タイプ自体を取得するには、そのタイプの値が必要です。

地図を使うというあなたのアイデアを採用しました。文字列を型自体にマップしますreflect.TypeOf。これは、インターフェイス値から型を取得する を使用して取得できます。次にreflect.Zero、その型のゼロ値 (すべての型に存在する便利な値) を取得していました。次に、値をインターフェイスとして取得しました。

package main
import "reflect"

type ta struct { a int }
type tb struct { b float64 }
type tc struct { c string }

var mActions map[string]reflect.Type = make(map[string]reflect.Type)
func init() {
  var a ta
  mActions[`ta`] = reflect.TypeOf(a)
  var b tb
  mActions[`tb`] = reflect.TypeOf(b)
  var c ta
  mActions[`tc`] = reflect.TypeOf(c)
}

func MagicVarFunc(action string) interface{} {
  return reflect.Zero(mActions[action]).Interface()
}

func main() {
  t := "tb"
  v := MagicVarFunc(t) // Returns a new allocated var of type interface{}
  x := v.(tb)
  x.b = 8.3
}
于 2012-06-21T01:56:48.013 に答える
2

ジョレリの答えはとても良いです。いくつかのオプションを紹介します。あなたの「本当の例」は本質的にコマンドディスパッチのように見え、コマンドパラメータはJSONで指定されています。これを行う簡単なコードから始めるには、

package main

import (
    "encoding/json"
    "fmt"
)

func inHandler(action string, data []byte) {
    arg := make(map[string]interface{})
    json.Unmarshal(data, &arg)
    switch action {
    case "say":
        fmt.Printf("You say, %q to %s\n", arg["msg"], arg["to"])
    case "exit":
        fmt.Println("Good bye")
    }
}

func main() {
    inHandler(`say`, []byte(`{"to":"Sonia","msg":"Please help me!"}`))
    inHandler(`exit`, []byte(`{}`))
}

switchステートメントにcaseを追加して、新しいコマンドを登録します。ええ、あなたがそれを望んでいるとは思わなかった。したがって、マップとinit()のアイデアを追加すると、

package main

import (
    "encoding/json"
    "fmt"
)

type jmap map[string]interface{}

var mActions = map[string]func(jmap){}

func init() {
    mActions["say"] = func(arg jmap) {
        fmt.Printf("You say, %q to %s\n", arg["msg"], arg["to"])
    }
}

func init() {
    mActions["exit"] = func(jmap) { fmt.Println("Good bye") }
}

func inHandler(action string, data []byte) {
    args := make(jmap)
    json.Unmarshal(data, &args)
    mActions[action](args)
}

func main() {
    inHandler(`say`, []byte(`{"to":"Sonia","msg":"Please help me!"}`))
    inHandler(`exit`, []byte(`{}`))
}

必要に応じて、これらのinit関数をそれぞれ個別のソースファイルに配置し、新しいinit関数を使用して新しいソースファイルを作成することで、新しいコマンドを登録できます。

プログラムの残りの部分は、JSONが常にオブジェクトとしてエンコードするフラットな引数リストがコマンドにあるといういくつかの仮定で単純化されています。これにより、コマンドごとに個別のGo構造体定義を省略できます。inHandlerは、すべてのコマンドに対して同じタイプのオブジェクト(マップ)を作成し、そのオブジェクトにアンマーシャリングして、コマンドに渡します。もう少し任意のJSONを処理したい場合は、空のインターフェースにマーシャリングを解除することができ、関数は引数を掘り下げるためにいくつかの追加作業を行う必要があります。それが大変な作業であり、構造体に直接アンマーシャリングしたい場合は、各コマンド関数に独自のJSONをアンマーシャリングさせるというjorelliのソリューションに近づきます。

于 2012-06-22T22:40:22.067 に答える
2

必要なことを実行する関数型を定義することから始めます。

type Producer func([]byte) interface{}

それらのいくつかを作ります:

func FooProducer(raw []byte) interface{} {
    foo := new(Foo)
    ... // do something to foo
    return foo
}

func BarProducter(raw []byte) interface{} {
    bar := new(Bar)
    ... // do something to bar
    return bar
}

それらを地図に貼り付けます。

likeThis := map[string]Producer{
    "foo": FooProducer,
    "bar": BarProducer,
}

次に、次のいずれかを実行します。

myVal := likeThis[someString](raw)

ただし、インターフェイスを定義して、プロデューサーを次のようなものにしたいと思うかもしれません。

type Producer func([]byte) MyAwesomeInterface

おそらく、デコードしているものでやりたいことがいくつかあるからです。また、次のように、文字列入力が正しくない場合も処理する必要があります。

f, ok := likeThis[someString]
if !ok {
    // return, break, panic... something, just get the hell away from here.
}
myVal := f(raw)

タイプを検査するという概念全体は、Goではちょっと面倒です。一般に、型システムを使用して反射体操を行うよりも、新しい型を追加する方が作業が少なくて済みます。

于 2012-06-21T04:22:29.350 に答える