0401
- 反射
- 收到电脑后在mac上安装环境:go,postgresql,echo
- 接口复习
函数 reflect.TypeOf()
接受任意的 interface{} 类型
并以reflect.Type形式返回其动态类型
fmt.Printf提供了一个缩写 %T 参数
内部使用 reflect.TypeOf 来输出
reflect.TypeOf 返回的是一个动态类型的接口值
reflect.ValueOf 的逆操作是
reflect.Value.Interface 方法
它返回一个 interface{} 类型
尽量避免在一个包的API中暴露涉及反射的接口
package main
import (
"bytes"
"fmt"
"reflect"
)
func encode(buf *bytes.Buffer,v reflect.Value) error{
switch v.Kind() {
case reflect.Invalid:
buf.WriteString("nil")
case reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64:
fmt.Fprintf(buf, "%d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
fmt.Fprintf(buf, "%d", v.Uint())
case reflect.String:
fmt.Fprintf(buf, "%q", v.String())
case reflect.Ptr:
return encode(buf, v.Elem())
case reflect.Array, reflect.Slice:
buf.WriteByte('(')
for i := 0;i < v.Len() ;i++ {
if i > 0 {
buf.WriteByte(' ')
}
if err := encode(buf, v.Index(i));err != nil {
return err
}
}
buf.WriteByte(')')
case reflect.Struct:
buf.WriteByte('(')
for i := 0;i<v.NumField() ;i++ {
if i > 0 {
buf.WriteByte(' ')
}
fmt.Fprintf(buf,"(%s ",v.Type().Field(i).Name)
if err := encode(buf, v.Field(i));err != nil {
return err
}
buf.WriteByte(')')
}
buf.WriteByte(')')
case reflect.Map: // ((key value) ...)
buf.WriteByte('(')
for i, key := range v.MapKeys() {
if i > 0 {
buf.WriteByte(' ')
}
buf.WriteByte('(')
if err := encode(buf, key); err != nil {
return err
}
buf.WriteByte(' ')
if err := encode(buf, v.MapIndex(key)); err != nil {
return err
}
buf.WriteByte(')')
}
buf.WriteByte(')')
case reflect.Float32, reflect.Float64:
fmt.Fprintf(buf, "%f", v.Float())
case reflect.Complex64, reflect.Complex128:
fmt.Fprintf(buf, "#C(%f %f)", real(v.Complex()),imag(v.Complex()))
case reflect.Bool:
va := v.Bool()
if va {
fmt.Fprintf(buf, "%v", "t")
}else {
fmt.Fprintf(buf, "%v", "nil")
}
default: // float, complex, bool, chan, func, interface
return fmt.Errorf("unsupported type: %s", v.Type())
}
return nil
}
func Marshal(v interface{}) ([]byte,error){
var buf bytes.Buffer
if err := encode(&buf, reflect.ValueOf(v)); err != nil {
return nil,err
}
return buf.Bytes(),nil
}
func main() {
var ints []int
ints = append(ints, 1,2,3,4,5)
serial,_ := Marshal(ints)
fmt.Printf("%s",serial)
}
通过reflect修改值
x := 2 // value type variable?
a := reflect.ValueOf(2) // 2 int no
b := reflect.ValueOf(x) // 2 int no
c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes (x)
无论原先是否是可取地址的,通过下面方式最后都可以得到可取地址的变量
px := d.Addr().Interface().(*int) // px := &x
或者,不使用指针,而是通过调用可取地址的reflect.Value
的reflect.Value.Set方法来更新对于的值:
d := reflect.ValueOf(&x).Elem() // d refers to the variable x
d.Set(reflect.ValueOf(4))
SetInt、SetUint、SetString和SetFloat等。
d := reflect.ValueOf(&x).Elem()
d.SetInt(3)
fmt.Println(x) // "3"
没有办法修改未导出的变量
获取结构体字段标签
package search
import (
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
)
func search(resp http.ResponseWriter, req *http.Request) {
var data struct {
Labels []string `http:"l"`
MaxResults int `http:"max"`
Exact bool `http:"x"`
}
data.MaxResults = 10 // set default
if err := params.Unpack(req, &data); err != nil {
http.Error(resp, err.Error(), http.StatusBadRequest) // 400
return
}
// ...rest of handler...
fmt.Fprintf(resp, "Search: %+v\n", data)
}
// Unpack populates the fields of the struct pointed to by ptr
// from the HTTP request parameters in req.
// 把req中的信息取出来通过反射放到ptr中
func Unpack(req *http.Request, ptr interface{}) error {
if err := req.ParseForm(); err != nil {
return err
}
// map{fieldname : field}
fields := make(map[string]reflect.Value)
v := reflect.ValueOf(ptr).Elem() // the struct variable
//给定v的所有field放入map中
for i := 0; i < v.NumField(); i++ {
fieldInfo := v.Type().Field(i) // a reflect.StructField
tag := fieldInfo.Tag // a reflect.StructTag
name := tag.Get("http")
if name == "" {
name = strings.ToLower(fieldInfo.Name)
}
fields[name] = v.Field(i)
}
// Update struct field for each parameter in the request.
for name, values := range req.Form {
f := fields[name]
if !f.IsValid() {
continue // ignore unrecognized HTTP parameters
}
for _, value := range values {
if f.Kind() == reflect.Slice {
elem := reflect.New(f.Type().Elem()).Elem()
if err := populate(elem, value); err != nil {
return fmt.Errorf("%s: %v", name, err)
}
f.Set(reflect.Append(f, elem))
} else {
if err := populate(f, value); err != nil {
return fmt.Errorf("%s: %v", name, err)
}
}
}
}
return nil
}
//把v的value中设置为value
func populate(v reflect.Value, value string) error {
switch v.Kind() {
case reflect.String:
v.SetString(value)
case reflect.Int:
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
v.SetInt(i)
case reflect.Bool:
b, err := strconv.ParseBool(value)
if err != nil {
return err
}
v.SetBool(b)
default:
return fmt.Errorf("unsupported kind %s", v.Type())
}
return nil
}
reflect.Type和reflect.Value都提供了一个Method方法
v.Method(i)方法调用都返回一个reflect.Value以表示对应的值
使用reflect.Value.Call方法将可以调用一个Func类型的Value
谨慎使用反射
底层编程
Go特性:
严格的类型转换规则
法知道一个结构体真实的内存布局
无法获取一个运行时函数对应的机器码
无法知道当前的goroutine是运行在哪个操作系统线程之上
一个指向变量的指针没有展示变量真实的地址
Go语言的这些特性使得Go程序相比较低级的C语言来说更容易预测和理解,程序也不容易崩溃
unsafe.Sizeof函数返回操作数在内存中的字节大小
参数可以是任意类型的表达式,但是它并不会对表达式进行求值
存在对齐的情况,如果多于8个字节,也就进行8地址对齐了
unsafe.Alignof 函数返回对应参数的类型需要对齐的倍数
Alignof 也是返回一个常量表达式, 对应一个常量.
通常情况下布尔和数字类型需要对齐到它们本身的大小(最多8个字节), 其它的类型对齐到机器字大小.
unsafe.Offsetof 函数的参数必须是一个字段 x.f,
返回 f 字段相对于 x 起始地址的偏移量
unsafe.Pointer
它可以包含任意类型变量的地址
一个普通的*T类型指针可以被转化为unsafe.Pointer类型指针
一个unsafe.Pointer类型指针也可以被转回普通的指针
被转回普通的指针类型**并不**需要和原始的*T类型相同
一个unsafe.Pointer指针也可以被转化为uintptr类型然后保存到指针型数值变量中然后用以做必要的指针数值运算。
上面这个特点应该谨慎使用,因为gc可能会导致内存地址变化,存的数值可能已经不是原来的那个变量了
深度相等判断
DeepEqual函数使用内建的==比较操作符对基础类型进行相等判断,
对于复合类型则递归该变量的每个基础类型然后做类似的比较判断
用DeepEqual函数比较两个字符串数组是否相等
func TestSplit(t *testing.T) {
got := strings.Split("a:b:c", ":")
want := []string{"a", "b", "c"};
if !reflect.DeepEqual(got, want) { /* ... */ }
}
一个nil值的map和非nil值但是空的map视作不相等
通过cgo调用C代码
其中import "C"的语句是比较特别的。其实并没有一个叫C的包,但是这行语句会让Go编译程序在编译之前先运行cgo工具。
这一块书中说的也比较简略,有很多的疑问,比如说C.uint和Go的uint就不相同,那么在通过C.x调用执行C方法的话,参数类型是否匹配呢?
也并没有查到说这个问题的,等用到的时候好好理解吧
复习一下接口
接口看的是其他人写的一份笔记,发现还是迷迷糊糊的,所以重新看一遍tgpl这一部分
接口是一个约定
package fmt
func Fprintf(w io.Writer, format string, args ...interface{}) (int, error)
func Printf(format string, args ...interface{}) (int, error) {
return Fprintf(os.Stdout, format, args...)
}
func Sprintf(format string, args ...interface{}) string {
var buf bytes.Buffer
Fprintf(&buf, format, args...)
return buf.String()
}
就以io.Writer为例子说明他们三个
Sprintf和Printf都是借用Fprintf来实现的
他们两个只要传入一个满足io.Writer()接口的类型变量就可以了
接口类型具体描述了一系列方法的集合
一个实现了这些方法的具体类型是这个接口类型的实例,并且可以像struct一样进行内嵌,
顺序并不重要,重要的是有哪些方法
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口
一些方法的接收者是类型T本身然而另一些则是一个*T的指针
在T类型的参数上调用一个*T的方法是合法的
但T类型的值不拥有所有*T指针的方法
type IntSet struct { /* ... */ }
func (*IntSet) String() string
var _ = IntSet{}.String() // compile error: String requires *IntSet receiver
接口类型封装和隐藏具体类型和它的值。即使具体类型有其它的方法,也只有接口类型暴露出来的方法会被调用到:
os.Stdout.Write([]byte("hello")) // OK: *os.File has Write method
os.Stdout.Close() // OK: *os.File has Close method
var w io.Writer
w = os.Stdout
w.Write([]byte("hello")) // OK: io.Writer has Write method
w.Close() // compile error: io.Writer lacks Close method
可以将任意一个值赋给空接口类型
接口值,由两个部分组成,一个具体的类型和那个类型的值
具体的类型:接口的动态类型
类型的值:动态值
排序例子:
package main
import (
"fmt"
"sort"
)
type People struct {
Name string
Age int
}
type Peoples []People
func (p Peoples) Len() int {
return len(p)
}
func (p Peoples) Less(i, j int) bool {
if p[i].Name != p[j].Name {
return p[i].Name < p[j].Name
} else {
return p[i].Age < p[j].Age
}
}
func (p Peoples) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func main() {
ps := Peoples{People{"a", 1}, People{"b", 2}, People{"b", 1}}
fmt.Println(ps)
sort.Sort(ps)
fmt.Println(ps)
}
类型断言
var w io.Writer
w = os.Stdout
f := w.(*os.File) // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer
可以定义一个只有这个方法的新接口并且使用类型断言来检测是否w的动态类型满足这个新接口
// writeString writes s to w.
// If w has a WriteString method, it is invoked instead of w.Write.
func writeString(w io.Writer, s string) (n int, err error) {
type stringWriter interface {
WriteString(string) (n int, err error)
}
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s) // avoid a copy
}
return w.Write([]byte(s)) // allocate temporary copy
}
func writeHeader(w io.Writer, contentType string) error {
if _, err := writeString(w, "Content-Type: "); err != nil {
return err
}
if _, err := writeString(w, contentType); err != nil {
return err
}
// ...
}