有没有办法从字符串创建结构的实例?

Ric*_*ard 45 go

给定一个结构:

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)

  • 您也可以使用`reflect.TypeOf(foo).Name()`而不是手动输入每个字符串. (10认同)
  • @Evan,使用`reflect.TypeOf(foo).String()更安全`看小例[here](http://play.golang.org/p/s1agPjsl4n). (3认同)
  • 我想我一直都知道要这么做了。我不希望在语言中添加这样的内容,因为有很多充分的理由不支持这种行为。 (2认同)

Dav*_*rth 9

您可以创建名称 - > 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)

  • 这看起来实际上非常顺利,避免了反射。 (2认同)
  • 它返回编译时定义的实例的副本。所以,从某种意义上来说,它们是新的。 (2认同)

dol*_*men 9

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)

  • 这是一个很好的答案!遗憾的是,它没有更多的赞誉。 (3认同)