Есть ли способ создать экземпляр структуры из строки?
С учетом структуры:
type MyStruct struct {
A int
B int
}
и строка с именем структуры
a := "MyStruct"
или
a := "mypkg.MyStruct"
Как создать экземпляр моей структуры из имени строки, а не структуры? Идея заключается в том, что я создам приложение со всеми структурами, связанными с двоичным кодом, но создам экземпляры среды выполнения из строк. (вид мета-мета)
Ответы
Ответ 1
В 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)
}
}
Затем вы можете создать экземпляры таких типов:
func makeInstance(name string) interface{} {
v := reflect.New(typeRegistry[name]).Elem()
// Maybe fill in fields here if necessary
return v.Interface()
}
Ответ 2
Вы можете создать карту имени → struct "template"
При захвате значения с карты вы получаете копию значения, карта эффективно действует как factory для ваших значений.
Обратите внимание, что значения из карты уникальны.
Чтобы на самом деле что-то сделать с помощью структуры, вам нужно либо утверждать ее тип, либо использовать некоторый процессор на основе отражений (т.е. Получить структуру с карты, а затем json декодировать в структуру)
Вот простой Пример с одной структурой в необработанной форме и одной предварительно заполненной.
Обратите внимание на утверждение типа на 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)
}
Ответ 3
Время выполнения 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"))
}