在 Go 中,如何在运行时根据对象的类型创建对象的实例? 我想您还需要先获取对象的实际
type
?
我正在尝试进行延迟实例化以节省内存。
为了做到这一点,你需要
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)
}
您可以使用结构类型而不是 int 执行相同的操作。或者其他什么,真的。当涉及到映射和切片类型时,请务必了解 new 和 make 之间的区别。
由于
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 was already made!
c.Meta.Users = append(c.Meta.Users, "srid") // so was the slice.
fmt.Println(v.Interface())
}
您可以使用
reflect.Zero()
它将返回结构类型的零值的表示形式。 (类似于如果你这样做了var foo StructType
)这与reflect.New()
不同,因为后者会动态分配结构并给你一个指针,类似于new(StructType)
这是 Evan Shaw 给出的一个基本示例,但带有一个结构:
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)
}
根据 newacct 的响应,使用 Reflect.zero 它将是:
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
这是一篇很棒的文章,介绍了 Go 中反射的基础知识。
你不需要
reflect
,如果它们共享相同的接口,你可以使用工厂模式轻松做到这一点:
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())
}
我自己偶然发现了相同(或非常相似)的问题。这是几个小时实验的结果,无论是否是指针,都会进行结构类型复制的解决方案:
func copyTypeInstance(v1 any) any {
typ := reflect.TypeOf(v1)
val := reflect.ValueOf(v1)
if val.Kind() == reflect.Pointer {
vp := reflect.New(typ.Elem())
return vp.Interface()
} else {
v := reflect.Zero(typ)
return v.Interface()
}
}
注意 - 不是深层复制 - 只是一个新实例,即一个空对象,就像您自己初始化它一样。这正是反序列化/解组目标所需要的。