Skip to the content.

1. 反射

反射是指在程序运行期对程序本身进行访问和修改的能力

1.1 变量的内在机制

1.2 反射的使用

1.3 空接口与反射

package main

import (
   "fmt"
   "reflect"
)

//反射获取interface类型信息

func main() {
   var x float64 = 3.4
   t := reflect.TypeOf(x)
   v := reflect.ValueOf(x)
   fmt.Println("类型是:", t,"值是:",v)
   k := t.Kind()
   fmt.Println("具体类型为:",k)
}
package main

import (
    "fmt"
    "reflect"
)

//反射修改值
func reflect_set_value(a interface{}) {
    v := reflect.ValueOf(a)
    k := v.Kind()
    switch k {
    case reflect.Float64:
        // 反射修改值
        v.SetFloat(6.9)
        fmt.Println("a is ", v.Float())
    case reflect.Ptr:
        // Elem()获取地址指向的值
        v.Elem().SetFloat(7.9)
        fmt.Println("case:", v.Elem().Float())
        // 地址
        fmt.Println(v.Pointer())
    }
}

func main() {
    var x float64 = 3.4
    // 反射认为下面是指针类型,不是float类型
    reflect_set_value(&x)
    fmt.Println("main:", x)
}

1.4 结构体与反射

通过反射得到sqlite建表语句

package main

import(
	"fmt"
	"reflect"
	"strings"
	"unicode"
)
type book struct {
	ID         uint64 `db:"id"`
	BookReview string `db:"bookreview"`
}

func main(){
	objptr := &book{

	}
	table := "book"
	var (
		tags  = tags(objptr)
		kinds = kinds(objptr)
		top   = len(tags) - 1
		cmd   = []string{}
	)
	if unicode.IsDigit([]rune(table)[0]) {
		table = "[" + table + "]"
	} else {
		table = "'" + table + "'"
	}
	cmd = append(cmd, "CREATE TABLE IF NOT EXISTS")
	cmd = append(cmd, table)
	cmd = append(cmd, "(")
	if top == 0 {
		cmd = append(cmd, tags[0])
		cmd = append(cmd, kinds[0])
		cmd = append(cmd, "PRIMARY KEY")
		cmd = append(cmd, "NOT NULL);")
	} else {
		for i := range tags {
			cmd = append(cmd, tags[i])
			cmd = append(cmd, kinds[i])
			switch i {
			default:
				cmd = append(cmd, "NULL,")
			case 0:
				cmd = append(cmd, "PRIMARY KEY")
				cmd = append(cmd, "NOT NULL,")
			case top:
				cmd = append(cmd, "NULL)")
			}
		}
	}
	fmt.Println(strings.Join(cmd, " ") + ";")
}

// tags 反射 返回结构体对象的 tag 数组
func tags(objptr interface{}) []string {
	var tags []string
	elem := reflect.ValueOf(objptr).Elem()
	for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
		t := elem.Type().Field(i).Tag.Get("db")
		if t == "" {
			t = elem.Type().Field(i).Tag.Get("json")
			if t == "" {
				t = elem.Type().Field(i).Name
			}
		}
		tags = append(tags, t)
	}
	return tags
}

// kinds 反射 返回结构体对象的 kinds 数组
func kinds(objptr interface{}) []string {
	var kinds []string
	elem := reflect.ValueOf(objptr).Elem()
	// 判断第一个元素是否为匿名字段
	if elem.Type().Field(0).Anonymous {
		elem = elem.Field(0)
	}
	for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
		switch elem.Field(i).Type().String() {
		case "bool":
			kinds = append(kinds, "BOOLEAN")
		case "int8":
			kinds = append(kinds, "TINYINT")
		case "uint8", "byte":
			kinds = append(kinds, "UNSIGNED TINYINT")
		case "int16":
			kinds = append(kinds, "SMALLINT")
		case "uint16":
			kinds = append(kinds, "UNSIGNED SMALLINT")
		case "int32":
			kinds = append(kinds, "INT")
		case "uint32":
			kinds = append(kinds, "UNSIGNED INT")
		case "int64":
			kinds = append(kinds, "BIGINT")
		case "uint64":
			kinds = append(kinds, "UNSIGNED BIGINT")
		default:
			kinds = append(kinds, "TEXT")
		}
	}
	return kinds
}

// values 反射 返回结构体对象的 values 数组
func values(objptr interface{}) []interface{} {
	var values []interface{}
	elem := reflect.ValueOf(objptr).Elem()
	for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
		if elem.Field(i).Type() == reflect.SliceOf(reflect.TypeOf("")) { // []string
			values = append(values, elem.Field(i).Index(0).Interface()) // string
			continue
		}
		values = append(values, elem.Field(i).Interface())
	}
	return values
}

// addrs 反射 返回结构体对象的 addrs 数组
func addrs(objptr interface{}) []interface{} {
	var addrs []interface{}
	elem := reflect.ValueOf(objptr).Elem()
	for i, flen := 0, elem.Type().NumField(); i < flen; i++ {
		if elem.Field(i).Type() == reflect.SliceOf(reflect.TypeOf("")) { // []string
			s := reflect.ValueOf(make([]string, 1))
			elem.Field(i).Set(s)
			addrs = append(addrs, s.Index(0).Addr().Interface()) // string
			continue
		}
		addrs = append(addrs, elem.Field(i).Addr().Interface())
	}
	return addrs
}

调用方法

package main

import (
    "fmt"
    "reflect"
)

// 定义结构体
type User struct {
    Id   int
    Name string
    Age  int
}

func (u User) Hello(name string) {
    fmt.Println("Hello:", name)
}

func main() {
    u := User{1, "5lmh.com", 20}
    v := reflect.ValueOf(u)
    // 获取方法
    m := v.MethodByName("Hello")
    // 构建一些参数
    args := []reflect.Value{reflect.ValueOf("6666")}
    // 没参数的情况下:var args2 []reflect.Value
    // 调用方法,需要传入方法的参数
    m.Call(args)
}

1.5 反射练习

#this is comment
;this a comment
;[]表示一个section
[server]
ip = 10.238.2.2
port = 8080

[mysql]
username = root
passwd = admin
database = test
host = 192.168.10.10
port = 8000
timeout = 1.2

PS: 我承认没有用反射,我还是喜欢用库

package main

import (
    "fmt"
    "gopkg.in/ini.v1"
    "log"
)

type Server struct{
	IP string
	Port string
}
type Mysql struct{
	Username string
	Passwd string
	Database string
	Host string
	Port string
	Timeout string
}

func main() {
    cfg, err := ini.Load("data/test/config.ini")
    getErr("load config", err)

    // 遍历所有的section
    for _, v := range cfg.Sections(){
        fmt.Println(v.KeyStrings())
    }

    // 获取默认分区的key
    fmt.Println(cfg.Section("server").Key("ip").String())    // 将结果转为string
    fmt.Println(cfg.Section("server").Key("port").String())     // 将结果转为float

    // 获取mysql分区的key
	fmt.Println(cfg.Section("mysql").Key("username").String())  // 将结果转为string
    fmt.Println(cfg.Section("mysql").Key("passwd").String())     // 将结果转为int
	fmt.Println(cfg.Section("mysql").Key("database").String())  // 将结果转为string
    fmt.Println(cfg.Section("mysql").Key("timeout").String())     // 将结果转为int
    fmt.Println(cfg.Section("mysql").Key("host").String())  // 将结果转为string
    fmt.Println(cfg.Section("mysql").Key("port").String())     // 将结果转为int
	s := &Server{
		IP:cfg.Section("server").Key("ip").String(),
		Port:cfg.Section("server").Key("port").String(),
	}
	m := &Mysql{
		Username:cfg.Section("mysql").Key("username").String(),
		Passwd:cfg.Section("mysql").Key("passwd").String(),
		Database:cfg.Section("mysql").Key("database").String(),
		Host:cfg.Section("mysql").Key("host").String(),
		Port:cfg.Section("mysql").Key("port").String(),
		Timeout:cfg.Section("mysql").Key("timeout").String(),
	}
	fmt.Println(s)
	fmt.Println(m)
}

func getErr(msg string, err error){
    if err != nil{
        log.Printf("%v err->%v\n", msg, err)
    }
}

2.1 结构体转map[string]interface{}

    m := Message{
		Type:     "note_on",
		Note:     60,
		Velocity: 64,
	}
	b, _ := json.Marshal(m)
	var mm map[string]interface{}
	_ = json.Unmarshal(b, &mm)

PS: 有个天坑,通过这样转化,结构体原来的int类型的字段在map中成了float64类型,非常不利于处理

package main

import (
	"fmt"
	"reflect"
)

type Message struct {
	Type       string  `json:"type"`
	Data       []byte  `json:"data"`
	Channel    int     `json:"channel"`
	Control    byte    `json:"control"`
	FrameType  int     `json:"frame_type"`
	FrameValue int     `json:"frame_value"`
	Note       byte    `json:"note"`
	Pitch      int     `json:"pitch"`
	Pos        int     `json:"pos"`
	Program    byte    `json:"program"`
	Song       byte    `json:"song"`
	Value      byte    `json:"value"`
	Velocity   byte    `json:"velocity"`
	Time       float64 `json:"time"`
}

func main() {
	m := Message{
		Type:     "note_on",
		Note:     60,
		Velocity: 64,
	}
	mm, err := ToMap(m, "json")
	if err != nil {
		fmt.Println(err)
	}
	for k, v := range mm {
		fmt.Printf("%v,%v,%T\n", k, v, v)
	}
}

func ToMap(in interface{}, tagName string) (out map[string]interface{}, err error) {
	out = make(map[string]interface{})
	v := reflect.ValueOf(in)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		err = fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
		return
	}
	t := v.Type()
	for i := 0; i < v.NumField(); i++ {
		fi := t.Field(i)
		if tagValue := fi.Tag.Get(tagName); tagValue != "" {
			out[tagValue] = v.Field(i).Interface()
		}
	}
	return
}