24
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

gumi.inc TS&DAdvent Calendar 2017

Day 11

Goで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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?