在运行时通过反射按名称动态实例化结构体。

huangapple go评论67阅读模式
英文:

Go dynamically instantiating struct by name at runtime via reflection

问题

在Go语言中,是否有类似于Java中动态类实例化功能的东西(注意:为了简洁起见,这里省略了异常处理逻辑):

cls := reflect.TypeOf(User{})
user := reflect.New(cls).Elem().Interface().(User)

上面的Go代码片段基本上通过提供的完全限定类路径字符串获取对类的引用,然后使用类引用获取对零参数构造函数(如果存在)的引用,最后使用构造函数获取对类的实例的引用。

我还没有找到在Go中实现类似结果的类似机制的示例。更具体地说,似乎Go中的reflect包要求调用者已经具有对他们希望实例化的结构体类型的引用。在这方面的标准惯用法如下:

reflect.New(reflect.TypeOf(User{}))

注意:传递给reflect.TypeOf函数的参数必须是Type而不是字符串。在Go中,是否可以仅通过完全限定的名称使用reflect包实例化结构体?

英文:

Is there anything equivalent in Go to the dynamic Class instantiation capabilities provided by languages like Java (note: requisite exception handling logic has been omitted here for the sake of brevity):

Class cls = Class.forName("org.company.domain.User");
Constructor<User> userConstructor = cls.getConstructor();
User user1 = userConstructor.newInstance();

The short Java snippet above essentially grabs reference to the Class via the supplied fully qualified classpath string, the class reference is then used to obtain reference to a zero-argument constructor (where one exists) and finally the constructor is used to obtain reference to an instance of the class.

I've yet to find an example of a similar mechanism in Go that may achieve similar results. More specifically, it would seem that the reflect package in go requires that the caller already have reference to the type of struct they wish to instantiate. The standard idiom in this regard seems to be as follows:

reflect.New(reflect.TypeOf(domain.User))

Note: The argument provided to the reflect.TypeOf function MUST be a Type not a string. Can a struct be instantiated in Go, via the reflect package, using nothing more than its fully qualified name?

答案1

得分: 5

Kubernetes在runtime.Scheme结构中处理这个过程。其思想是你可以注册带有名称或其他标识符的类型,然后根据标识符随时请求这些类型的新实例。一般来说,这个标识符是在序列化过程中派生的,而不是硬编码到源代码中。

问题在于,正如你所说,你需要最初创建一个新实例。虽然这种模式并不常见,但在我的职业生涯中,我遇到过两种情况,这是逻辑上的解决方案。下面是一个非常简化版本的K8s runtime.Scheme的示例,它可能适用于你尝试做的事情,你可以在这里查看它的运行效果:

package main

import (
	"fmt"
	"reflect"
)

type Scheme struct {
	types map[string]reflect.Type
}

func (s *Scheme) RegisterType(name string, t interface{}) {
	a := reflect.TypeOf(t)
	s.types[name] = a
}

func (s *Scheme) New(name string) (interface{}, error) {
	t, ok := s.types[name]
	if !ok {
		return nil, fmt.Errorf("unrecognized type name: %s", name)
	}
	return reflect.New(t).Interface(), nil
}

func NewScheme() *Scheme {
	return &Scheme{types: map[string]reflect.Type{}}
}

type MyType struct {
	Foo string
}

func main() {
	scheme := NewScheme()
	scheme.RegisterType("my.type", MyType{})
	myType, _ := scheme.New("my.type")
	myType.(*MyType).Foo = "bar"
	fmt.Println("%+v", myType)
}

希望对你有所帮助!

英文:

Kubernetes handles this exact process in the runtime.Scheme structure. The idea is that you register types with names or some other identifier, then you can ask for new instances of those types at will based on the identifier. Generally speaking this identifier is derived during a serialization process for example, rather then hard-coded into the source.

The catch is as you said, you need to create a new instance initially. While this pattern is un-common, I have come across two cases in my professional career where this was the logical solution. Here is an example of a very stripped down version of what the K8s runtime.Scheme does to accomplish this and it may work for what you're trying to do and here it is in action:

package main

import (
	"fmt"
	"reflect"
)

type Scheme struct {
	types map[string]reflect.Type
}

func (s *Scheme) RegisterType(name string, t interface{}) {
	a := reflect.TypeOf(t)
	s.types[name] = a
}

func (s *Scheme) New(name string) (interface{}, error) {
	t, ok := s.types[name]
	if !ok {
		return nil, fmt.Errorf("unrecognized type name: %s", name)
	}
	return reflect.New(t).Interface(), nil
}

func NewScheme() *Scheme {
	return &Scheme{types: map[string]reflect.Type{}}
}

type MyType struct {
	Foo string
}

func main() {
	scheme := NewScheme()
	scheme.RegisterType("my.type", MyType{})
	myType, _ := scheme.New("my.type")
	myType.(*MyType).Foo = "bar"
	fmt.Println("%+v", myType)
}

huangapple
  • 本文由 发表于 2021年6月9日 00:16:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/67890817.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定