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

后端 未结 5 1447
清歌不尽
清歌不尽 2020-12-02 06:07

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?

相关标签:
5条回答
  • 2020-12-02 06:12

    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.

    0 讨论(0)
  • 2020-12-02 06:13

    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())
    }
    
    0 讨论(0)
  • 2020-12-02 06:14

    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)

    0 讨论(0)
  • 2020-12-02 06:21

    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.

    0 讨论(0)
  • 2020-12-02 06:32

    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())
    }
    
    0 讨论(0)
提交回复
热议问题