有没有办法从string创build一个结构的实例?
给定一个结构体:
type MyStruct struct { A int B int }
和一个结构名称的string
a := "MyStruct"
要么
a := "mypkg.MyStruct"
如何从string名称而不是结构创build我的结构实例? 这个想法是,我将创build一个应用程序与所有链接到二进制文件的结构,但创build从string的运行时实例。 (一种元元)
Go中没有types的中央registry,所以在一般情况下你所要求的是不可能的。
您可以手动构build您自己的registry,以支持使用string映射来reflect.Type
这种function。每种types对应的types值。 例如:
var typeRegistry = make(map[string]reflect.Type) func init() { typeRegistry["MyString"] = reflect.TypeOf(MyString{}) ... }
然后你可以像这样创buildtypes的实例:
func makeInstance(name string) interface{} { v := reflect.New(typeRegistry[name]).Elem() // Maybe fill in fields here if necessary return v.Interface() }
你可以创build一个名称映射 – >结构“模板”
当从地图上获取一个值时,您会得到该值的一个副本,地图可以充当您的值的工厂。
请注意,来自地图的值是唯一的。 为了实际上对结构做些什么,你需要断言它的types或者使用一些基于reflection的处理器(例如:get map from map,然后json解码到结构中)
下面是一个简单的示例 ,其中一个结构是原始forms,一个是预先填充的。注意foowv1上的断言types,这样我就可以真正设置这个值。
package main import "fmt" type foo struct { a int } var factory map[string]interface{} = map[string]interface{}{ "foo": foo{}, "foo.with.val": foo{2}, } func main() { foo1 := factory["foo"] foo2 := factory["foo"] fmt.Println("foo1", &foo1, foo1) fmt.Println("foo2", &foo2, foo2) foowv1 := factory["foo.with.val"].(foo) foowv1.a = 123 foowv2 := factory["foo.with.val"] fmt.Println("foowv1", &foowv1, foowv1) fmt.Println("foowv2", &foowv2, foowv2) }
Go运行时不会公开程序中内置的types列表。 有一个原因:你永远不必能够build立所有可用的types,而只是一个子集。
您可以使用地图来build立自己的子集。 您可以使用reflect
包从reflect.Type
创build一个实例。
我的解决scheme( 参见Go Playground )使用键入的nil指针(而不是空值)来减less构build映射时的分配大小(与@ james-henstridge解决scheme相比)。
package main import ( "fmt" "reflect" ) var typeRegistry = make(map[string]reflect.Type) func registerType(typedNil interface{}) { t := reflect.TypeOf(typedNil).Elem() typeRegistry[t.PkgPath() + "." + t.Name()] = t } type MyString string type myString string func init() { registerType((*MyString)(nil)) registerType((*myString)(nil)) // ... } func makeInstance(name string) interface{} { return reflect.New(typeRegistry[name]).Elem().Interface() } func main() { for k := range typeRegistry { fmt.Println(k) } fmt.Printf("%T\n", makeInstance("main.MyString")) fmt.Printf("%T\n", makeInstance("main.myString")) }