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

发布于 2024-12-11 11:22:26 字数 80 浏览 0 评论 0原文

在 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

叹沉浮 2024-12-18 11:22:26

为此,您需要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 之间的区别。

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.

惟欲睡 2024-12-18 11:22:26

由于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())
}

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())
}
荆棘i 2024-12-18 11:22:26

您可以使用reflect.Zero()它将返回结构类型零值的表示形式。 (与 var foo StructType 类似)这与 reflect.New() 不同,因为后者会动态分配结构并为您提供一个指针,类似于 <代码>新(结构类型)

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)

勿忘初心 2024-12-18 11:22:26

这是 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 反射基础知识的精彩文章

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.

心房敞 2024-12-18 11:22:26

您不需要 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())
}

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())
}
埋情葬爱 2024-12-18 11:22:26

自己偶然发现了相同(或非常相似)的问题。这是几个小时实验的结果,无论它是否是指针,都会进行结构类型复制的解决方案:

    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()
        }
    }

注意 - 不是深层复制 - 只是一个新实例,即一个空对象,就像你初始化它一样你自己。这正是反序列化/解组目标所需要的。

完整演示:https://go.dev/play/p/vHFViKJZlV9

Stumbled myself on to the same (or very similar) problem. Here's the result of few hours' experiments, the solution that makes a struct type copy, no matter whether it is a pointer or not:

    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()
        }
    }

Note - not a deep copy - just a new instance, i.e. an empty object, as if you inited it yourself. That's exactly what is wanted as a deserialization/unmarshalling target.

The full demo: https://go.dev/play/p/vHFViKJZlV9

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文