如何在Go中在运行时根据类型创建一个结构体的新实例?

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

How do you create a new instance of a struct from its type at run time in Go?

问题

在Go语言中,你如何在运行时根据类型创建对象的实例?我想你也需要先获取对象的实际类型吧?

我正在尝试进行延迟实例化以节省内存。

英文:

In Go, how do you create the instance of an object from its type at run time? I suppose you would also need to get the actual type of the object first too?

I am trying to do lazy instantiation to save memory.

答案1

得分: 106

为了做到这一点,你需要使用reflect

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // 一种方法是已经有一个你想要的类型的值
    a := 1
    // reflect.New 的工作方式有点像内置函数 new
    // 我们将得到一个反射指针,指向一个新的 int 值
    intPtr := reflect.New(reflect.TypeOf(a))
    // 为了证明它
    b := intPtr.Elem().Interface().(int)
    // 打印 0
    fmt.Println(b)

    // 我们也可以使用 reflect.New 而不需要一个类型的值
    var nilInt *int
    intType := reflect.TypeOf(nilInt).Elem()
    intPtr2 := reflect.New(intType)
    // 与上面相同
    c := intPtr2.Elem().Interface().(int)
    // 再次打印 0
    fmt.Println(c)
}

你可以使用结构体类型来做同样的事情,而不是 int。或者其他任何类型,只要确保在处理 map 和 slice 类型时了解 new 和 make 之间的区别。

英文:

In order to do that you need reflect.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    // one way is to have a value of the type you want already
    a := 1
    // reflect.New works kind of like the built-in function new
    // We'll get a reflected pointer to a new int value
    intPtr := reflect.New(reflect.TypeOf(a))
    // Just to prove it
    b := intPtr.Elem().Interface().(int)
    // Prints 0
    fmt.Println(b)

    // We can also use reflect.New without having a value of the type
    var nilInt *int
    intType := reflect.TypeOf(nilInt).Elem()
    intPtr2 := reflect.New(intType)
    // Same as above
    c := intPtr2.Elem().Interface().(int)
    // Prints 0 again
    fmt.Println(c)
}

You can do the same thing with a struct type instead of an int. Or anything else, really. Just be sure to know the distinction between new and make when it comes to map and slice types.

答案2

得分: 44

作为reflect.New不会自动创建在结构体字段中使用的引用类型,您可以使用以下代码来递归初始化这些字段类型(请注意此示例中的递归结构体定义):

package main

import (
	"fmt"
	"reflect"
)

type Config struct {
	Name string
	Meta struct {
		Desc       string
		Properties map[string]string
		Users      []string
	}
}

func initializeStruct(t reflect.Type, v reflect.Value) {
	for i := 0; i < v.NumField(); i++ {
		f := v.Field(i)
		ft := t.Field(i)
		switch ft.Type.Kind() {
		case reflect.Map:
			f.Set(reflect.MakeMap(ft.Type))
		case reflect.Slice:
			f.Set(reflect.MakeSlice(ft.Type, 0, 0))
		case reflect.Chan:
			f.Set(reflect.MakeChan(ft.Type, 0))
		case reflect.Struct:
			initializeStruct(ft.Type, f)
		case reflect.Ptr:
			fv := reflect.New(ft.Type.Elem())
			initializeStruct(ft.Type.Elem(), fv.Elem())
			f.Set(fv)
		default:
		}
	}
}

func main() {
	t := reflect.TypeOf(Config{})
	v := reflect.New(t)
	initializeStruct(t, v.Elem())
	c := v.Interface().(*Config)
	c.Meta.Properties["color"] = "red" // map已经创建!
	c.Meta.Users = append(c.Meta.Users, "srid") // 切片也是如此。
	fmt.Println(v.Interface())
}
英文:

As reflect.New doesn't automatically make reference types used in struct fields, you could use something like the following to recursively initialize those field types (note the recursive struct definition in this example):

package main
import (
&quot;fmt&quot;
&quot;reflect&quot;
)
type Config struct {
Name string
Meta struct {
Desc string
Properties map[string]string
Users []string
}
}
func initializeStruct(t reflect.Type, v reflect.Value) {
for i := 0; i &lt; v.NumField(); i++ {
f := v.Field(i)
ft := t.Field(i)
switch ft.Type.Kind() {
case reflect.Map:
f.Set(reflect.MakeMap(ft.Type))
case reflect.Slice:
f.Set(reflect.MakeSlice(ft.Type, 0, 0))
case reflect.Chan:
f.Set(reflect.MakeChan(ft.Type, 0))
case reflect.Struct:
initializeStruct(ft.Type, f)
case reflect.Ptr:
fv := reflect.New(ft.Type.Elem())
initializeStruct(ft.Type.Elem(), fv.Elem())
f.Set(fv)
default:
}
}
}
func main() {
t := reflect.TypeOf(Config{})
v := reflect.New(t)
initializeStruct(t, v.Elem())
c := v.Interface().(*Config)
c.Meta.Properties[&quot;color&quot;] = &quot;red&quot; // map was already made!
c.Meta.Users = append(c.Meta.Users, &quot;srid&quot;) // so was the slice.
fmt.Println(v.Interface())
}

答案3

得分: 25

你可以使用reflect.Zero(),它将返回结构体类型的零值表示(类似于var foo StructType)。这与reflect.New()不同,后者将动态分配结构体并给你一个指针,类似于new(StructType)

英文:

You can use reflect.Zero() which will return the representation of the zero value of the struct type. (similar to if you did var foo StructType) This is different from reflect.New() as the latter will dynamically allocate the struct and give you a pointer, similar to new(StructType)

答案4

得分: 19

这是一个基本的示例,类似于Evan Shaw给出的示例,但使用了一个结构体:

package main

import (
	"fmt"
	"reflect"
)

func main() {

	type Product struct {
		Name  string
		Price string
	}

	var product Product
	productType := reflect.TypeOf(product)       // 这个变量的类型是reflect.Type
	productPointer := reflect.New(productType)   // 这个变量的类型是reflect.Value
	productValue := productPointer.Elem()        // 这个变量的类型是reflect.Value
	productInterface := productValue.Interface() // 这个变量的类型是interface{}
	product2 := productInterface.(Product)       // 这个变量的类型是Product

	product2.Name = "Toothbrush"
	product2.Price = "2.50"

	fmt.Println(product2.Name)
	fmt.Println(product2.Price)

}

根据newacct的回答,使用Reflect.zero的话,代码如下:

var product Product
productType := reflect.TypeOf(product)       // 这个变量的类型是reflect.Type
productValue := reflect.Zero(productType)    // 这个变量的类型是reflect.Value
productInterface := productValue.Interface() // 这个变量的类型是interface{}
product2 := productInterface.(Product)       // 这个变量的类型是Product

这是关于Go语言反射基础的一篇很棒的文章

英文:

Here's a basic example like Evan Shaw gave, but with a struct:

package main
import (
&quot;fmt&quot;
&quot;reflect&quot;
)
func main() {
type Product struct {
Name  string
Price string
}
var product Product
productType := reflect.TypeOf(product)       // this type of this variable is reflect.Type
productPointer := reflect.New(productType)   // this type of this variable is reflect.Value. 
productValue := productPointer.Elem()        // this type of this variable is reflect.Value.
productInterface := productValue.Interface() // this type of this variable is interface{}
product2 := productInterface.(Product)       // this type of this variable is product
product2.Name = &quot;Toothbrush&quot;
product2.Price = &quot;2.50&quot;
fmt.Println(product2.Name)
fmt.Println(product2.Price)
}

Per newacct's response, using Reflect.zero it would be:

   var product Product
productType := reflect.TypeOf(product)       // this type of this variable is reflect.Type
productValue := reflect.Zero(productType)    // this type of this variable is reflect.Value
productInterface := productValue.Interface() // this type of this variable is interface{}
product2 := productInterface.(Product)       // the type of this variable is Product

This is a great article on the basics of reflection in go.

答案5

得分: 9

你不需要使用reflect,如果它们共享相同的接口,你可以使用工厂模式轻松实现这一点:

package main
import (
"fmt"
)
// 所有类的共同接口
type MainInterface interface {
GetId() string
}
// 第一种类型的对象
type FirstType struct {
Id string
}
func (ft *FirstType) GetId() string {
return ft.Id
}
// FirstType 工厂
func InitializeFirstType(id string) MainInterface {
return &FirstType{Id: id}
}
// 第二种类型的对象
type SecondType struct {
Id string
}
func (st *SecondType) GetId() string {
return st.Id
}
// SecondType 工厂
func InitializeSecondType(id string) MainInterface {
return &SecondType{Id: id}
}
func main() {
// 字符串到工厂函数的映射
classes := map[string]func(string) MainInterface{
"first": InitializeFirstType,
"second": InitializeSecondType,
}
// 使用工厂函数创建一个新的 FirstType 对象,值为 10
newObject := classes["first"]("10")
// 显示正确创建的对象
fmt.Printf("%v\n", newObject.GetId())
// 使用工厂函数创建一个新的 SecondType 对象,值为 20
newObject2 := classes["second"]("20")
// 显示正确创建的对象
fmt.Printf("%v\n", newObject2.GetId())
}
英文:

You don't need reflect and you can do this easy with factory pattern if they share the same interface:

package main
import (
&quot;fmt&quot;
)
// Interface common for all classes
type MainInterface interface {
GetId() string
}
// First type of object
type FirstType struct {
Id string
}
func (ft *FirstType) GetId() string {
return ft.Id
}
// FirstType factory
func InitializeFirstType(id string) MainInterface {
return &amp;FirstType{Id: id}
}
// Second type of object
type SecondType struct {
Id string
}
func (st *SecondType) GetId() string {
return st.Id
}
// SecondType factory
func InitializeSecondType(id string) MainInterface {
return &amp;SecondType{Id: id}
}
func main() {
// Map of strings to factories
classes := map[string]func(string) MainInterface{
&quot;first&quot;: InitializeFirstType,
&quot;second&quot;: InitializeSecondType,
}
// Create a new FirstType object with value of 10 using the factory
newObject := classes[&quot;first&quot;](&quot;10&quot;)
// Show that we have the object correctly created
fmt.Printf(&quot;%v\n&quot;, newObject.GetId())
// Create a new SecondType object with value of 20 using the factory
newObject2 := classes[&quot;second&quot;](&quot;20&quot;)
// Show that we have the object correctly created
fmt.Printf(&quot;%v\n&quot;, newObject2.GetId())
}

huangapple
  • 本文由 发表于 2011年10月21日 21:32:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/7850140.html
匿名

发表评论

匿名网友

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

确定