0401

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
    }
    // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值