LoginSignup
24
20

More than 5 years have passed since last update.

golangでStructを動的に作る方法。

Last updated at Posted at 2017-12-11

どのような使い道があるのかはさておき、reflectパッケージを使って動的に任意のFieldを持つStructを作ることができる。

package main

import (
    "fmt"
    "reflect"
)

type StructBuilder struct {
    field []reflect.StructField
}

func NewStructBuilder() *StructBuilder {
    return &StructBuilder{}
}

func (b *StructBuilder) AddField(fname string, ftype reflect.Type) {
    b.field = append(
        b.field,
        reflect.StructField{
            Name: fname,
            Type: ftype,
        })
}

func (b *StructBuilder) Build() Struct {
    strct := reflect.StructOf(b.field)
    index := make(map[string]int)
    for i := 0; i < strct.NumField(); i++ {
        index[strct.Field(i).Name] = i
    }
    return Struct{strct, index}
}

type Struct struct {
    strct reflect.Type
    index map[string]int
}

func (s *Struct) NewInstance() *Instance {
    instance := reflect.New(s.strct).Elem()
    return &Instance{instance, s.index}
}

type Instance struct {
    internal reflect.Value
    index    map[string]int
}

func (i *Instance) Field(name string) reflect.Value {
    return i.internal.Field(i.index[name])
}

func (i *Instance) SetString(name, value string) {
    i.Field(name).SetString(value)
}

func (i *Instance) SetBool(name string, value bool) {
    i.Field(name).SetBool(value)
}

func (i *Instance) SetInt(name string, value int) {
    i.Field(name).SetInt(int64(value))
}

func (i *Instance) SetFloat(name string, value float64) {
    i.Field(name).SetFloat(value)
}

func (i *Instance) Value() interface{} {
    return i.internal.Interface()
}

func (i *Instance) Pointer() interface{} {
    return i.internal.Addr().Interface()
}

func main() {
    b := NewStructBuilder()
    b.AddField("Name", reflect.TypeOf("")) //型情報が欲しいだけなので、reflect.TypeOf()の入力値はなんでもいい。
    b.AddField("Age", reflect.TypeOf(123))
    person := b.Build()

    i := person.NewInstance()
    i.SetString("Name", "gopher")
    i.SetInt("Age", 8)

    fmt.Println(i.Value())   // -> {gopher 8}
    fmt.Println(i.Pointer()) // -> &{gopher 8}
}
  1. reflect.StructField{}でFieldを定義する。
  2. StructFieldをSliceでまとめたものをreflect.StructOf()に渡すとStructが生成される。
  3. このStructをreflect.New()に渡すとインスタンスができる。
  4. SetXXXで値をつめて
  5. Interface()Addr().Interface()でおなじみの形式に変換。

ORMなどのリフレクション前提のライブラリで使い道がある...かも

参考

  1. reflect - The Go Programming Language
  2. The Go Playground
24
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
20