Подтвердить что ты не робот

Есть ли способ создать экземпляр структуры из строки?

С учетом структуры:

type MyStruct struct {
    A int
    B int
}

и строка с именем структуры

a := "MyStruct"

или

a := "mypkg.MyStruct"

Как создать экземпляр моей структуры из имени строки, а не структуры? Идея заключается в том, что я создам приложение со всеми структурами, связанными с двоичным кодом, но создам экземпляры среды выполнения из строк. (вид мета-мета)

4b9b3361

Ответ 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"))
}