Go一些tips

Go语言的类型检查有两种技术,一种是 Type Assert,一种是Reflection

go的类型检查

Type Assert 转型操作

对某个变量进行 .(type)的转型操作,

会返回两个值 variable, error

第一个返回值是被转换好的类型

第二个是如果不能转换类型,则会报错
package main_test

import "testing"

type Container []interface{}

func (c *Container) Put(ele interface{}){
	*c = append(*c, ele)
}

func (c *Container) Get() interface{}{
	ele := (*c)[0]
	*c = (*c)[1:]
	return ele
}

func TestPut(t *testing.T){
	intContainer := &Container{}
	intContainer.Put(8)
	intContainer.Put(42)

	ele, ok := intContainer.Get().(int)
	// 把数据取出来时,因为类型是 interface{} 
	// 所以,你还要做一个转型,如果转型成功能才能进行后续操作(因为 interface{}太泛了,泛到什么类型都可以放)

	if !ok{
		t.Log("不是一个整数")
	}

	t.Log(ele)
}

Reflection 反射

package main_test

import (
	"fmt"
	"reflect"
	"testing"
)

type Container struct{
	s reflect.Value
}

// NewContainer()会根据参数的类型初始化一个Slice
func NewContainer(t reflect.Type, size int) *Container {
	if size <=0 {size=64}
	return &Container{
		s: reflect.MakeSlice(reflect.SliceOf(t), 0, size),
	}
}

//  Put()时候,会检查 val 是否和Slice的类型一致
func (c *Container) Put(val interface{}) error{
	if reflect.ValueOf(val).Type() != c.s.Type().Elem(){
		return fmt.Errorf("put : cannot put a %T into a slice of %s",
			val, c.s.Type().Elem())
	}
	c.s = reflect.Append(c.s, reflect.ValueOf(val))
	return nil
}

// Get()时,我们需要用一个入参的方式,因为我们没有办法返回 reflect.Value 或是 interface{},不然还要做Type Assert
func (c *Container) Get(refval interface{}) error{
	if reflect.ValueOf(refval).Kind() != reflect.Ptr ||
		reflect.ValueOf(refval).Elem().Type() != c.s.Type().Elem(){
		return fmt.Errorf(
			"Get: needs *%s but got a %T", c.s.Type().Elem(), refval)
	}
	reflect.ValueOf(refval).Elem().Set(c.s.Index(0))
	c.s = c.s.Slice(1, c.s.Len())
	return nil
}


func TestReflection(t *testing.T){
	f1 := 3.1415926
	f2 := 1.41421356237
	c := NewContainer(reflect.TypeOf(f1), 16)

	if err := c.Put(f1); err != nil {
		panic(err)
	}
	if err := c.Put(f2); err != nil {
		panic(err)
	}

	g := 12
	if err := c.Get(&g); err != nil {
		panic(err)
	}
	fmt.Printf("%v (%T)\n", g, g) //3.1415926 (float64)
	fmt.Println(c.s.Index(0)) //1.4142135623
}

Go Generator(模板)

C++ 模板的具体化

C++的编译器会在编译时分析代码,
根据不同的变量类型来自动化的生成相关类型的函数或类
//用<class T>来描述泛型
template <class T> 
T GetMax (T a, T b)  { 
    T result; 
    result = (a>b)? a : b; 
    return (result); 
} 

int i=5, j=6, k; 
//生成int类型的函数
k=GetMax<int>(i,j);
 
long l=10, m=5, n; 
//生成long类型的函数
n=GetMax<long>(l,m); 

Go的代码生成 需要三件事:

一个函数模板,其中设置好相应的占位符。
filter.tmp.go

package PACKAGE_NAME
type GENERIC_NAMEList []GENERIC_TYPE
type GENERIC_NAMEToBool func(*GENERIC_TYPE) bool
func (al GENERIC_NAMEList) Filter(f GENERIC_NAMEToBool) GENERIC_NAMEList {
    var ret GENERIC_NAMEList
    for _, a := range al {
        if f(&a) {
            ret = append(ret, a)
        }
    }
    return ret
}

一个脚本,用于按规则来替换文本并生成新的代码。
#!/bin/bash
set -e
SRC_FILE=${1}
PACKAGE=${2}
TYPE=${3}
DES=${4}
#uppcase the first char
PREFIX="$(tr '[:lower:]' '[:upper:]' <<< ${TYPE:0:1})${TYPE:1}"
DES_FILE=$(echo ${TYPE}| tr '[:upper:]' '[:lower:]')_${DES}.go
sed 's/PACKAGE_NAME/'"${PACKAGE}"'/g' ${SRC_FILE} | \
    sed 's/GENERIC_TYPE/'"${TYPE}"'/g' | \
    sed 's/GENERIC_NAME/'"${PREFIX}"'/g' > ${DES_FILE}

//4个参数:

//模板源文件
//包名
//实际需要具体化的类型
//用于构造目标文件名的后缀
一行注释代码。
type Employee struct {
  Name     string
  Age      int
  Vacation int
  Salary   int
}
//go:generate ./gen.sh ./template/filter.tmp.go gen Employee filter
func filterEmployeeExample() {
  var list = EmployeeList{
    {"Hao", 44, 0, 8000},
    {"Bob", 34, 10, 5000},
    {"Alice", 23, 5, 9000},
    {"Jack", 26, 0, 4000},
    {"Tom", 48, 9, 7500},
  }
  var filter EmployeeList
  filter = list.Filter(func(e *Employee) bool {
    return e.Age > 40
  })
  fmt.Println("----- Employee.Age > 40 ------")
  for _, e := range filter {
    fmt.Println(e)
  }
  filter = list.Filter(func(e *Employee) bool {
    return e.Salary <= 5000
  })
  fmt.Println("----- Employee.Salary <= 5000 ------")
  for _, e := range filter {
    fmt.Println(e)
  }
}

酷 壳 – COOLSHELL – Go Generator


转自酷 壳 – COOLSHELL

Buy me a 肥仔水!