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

在 Go 中,如何在运行时根据对象的类型创建对象的实例?我想你也需要得到实际的 type的对象呢?

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

82737 次浏览

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.

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)

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 (
"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 was already made!
c.Meta.Users = append(c.Meta.Users, "srid") // so was the slice.
fmt.Println(v.Interface())
}

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

package main


import (
"fmt"
"reflect"
)


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 = "Toothbrush"
product2.Price = "2.50"


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.

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

package main


import (
"fmt"
)


// 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 &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 &SecondType{Id: id}
}




func main() {
// Map of strings to factories
classes := map[string]func(string) MainInterface{
"first": InitializeFirstType,
"second": InitializeSecondType,
}


// Create a new FirstType object with value of 10 using the factory
newObject := classes["first"]("10")


// Show that we have the object correctly created
fmt.Printf("%v\n", newObject.GetId())




// Create a new SecondType object with value of 20 using the factory
newObject2 := classes["second"]("20")


// Show that we have the object correctly created
fmt.Printf("%v\n", newObject2.GetId())
}