Как вы создаете новый экземпляр структуры из ее типа во время выполнения в Go? - PullRequest
56 голосов
/ 21 октября 2011

В Go, как вы создаете экземпляр объекта из его типа во время выполнения?Я полагаю, вам также нужно сначала получить фактический type объекта?

Я пытаюсь выполнить ленивый экземпляр для экономии памяти.

Ответы [ 5 ]

67 голосов
/ 22 октября 2011

Для этого вам нужно 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, когда дело доходит до карт и типов срезов.

26 голосов
/ 24 апреля 2013

Поскольку 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())
}
16 голосов
/ 22 октября 2011

Вы можете использовать reflect.Zero(), который будет возвращать представление нулевого значения типа struct. (аналогично тому, как вы это сделали var foo StructType). Это отличается от reflect.New(), так как последний будет динамически выделять структуру и давать вам указатель, аналогично new(StructType)

3 голосов
/ 09 февраля 2018

Вот базовый пример, который дал Эван Шоу, но со структурой:

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

Это отличная статья об основах рефлексии в го.

2 голосов
/ 18 июня 2018

Вам не нужно 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())
}
...