给定一个结构:
type MyStruct struct {
A int
B int
}
Run Code Online (Sandbox Code Playgroud)
和一个带结构名称的字符串
a := "MyStruct"
Run Code Online (Sandbox Code Playgroud)
要么
a := "mypkg.MyStruct"
Run Code Online (Sandbox Code Playgroud)
如何从字符串名称而不是结构创建结构的实例?我的想法是创建一个应用程序,其中所有结构都链接到二进制文件中,但是从字符串创建运行时实例.(一种元元)
Jam*_*dge 64
Go中没有类型的中央注册表,因此在一般情况下你所要求的是不可能的.
您可以手动构建自己的注册表,以使用从字符串reflect.Type
到每种类型对应的值的映射来支持此类功能.例如:
var typeRegistry = make(map[string]reflect.Type)
func init() {
myTypes := []interface{}{MyString{}}
for _, v := range myTypes {
// typeRegistry["MyString"] = reflect.TypeOf(MyString{})
typeRegistry[fmt.Sprintf("%T", v)] = reflect.TypeOf(v)
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以创建类型的实例,如下所示:
func makeInstance(name string) interface{} {
v := reflect.New(typeRegistry[name]).Elem()
// Maybe fill in fields here if necessary
return v.Interface()
}
Run Code Online (Sandbox Code Playgroud)
您可以创建名称 - > struct"template"的地图
从地图中获取值时,您会获得该值的副本,该地图有效地充当您的值的工厂.
请注意,地图中的值是唯一的.为了实际对结构做一些事情,你需要断言它的类型或使用一些基于反射的处理器(即:从map获取struct,然后json解码到struct)
这是一个简单的示例,其中一个原始形式的结构和一个预填充的结构.请注意foowv1上的类型断言,这样我实际上可以设置该值.
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)
}
Run Code Online (Sandbox Code Playgroud)
Go运行时不会公开程序内置的类型列表。这是有原因的:您不必构建所有可用类型,而只需构建一个子集即可。
您可以使用地图构建此子集。您可以使用该reflect
包从中创建实例reflect.Type
。
我的解决方案(请参阅Go Playground上的文章)使用类型化的nil指针(而不是空值)来减少构建地图时的分配大小(与@ james-henstridge解决方案相比)。
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"))
}
Run Code Online (Sandbox Code Playgroud)