🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 技巧 ### 位运算做参数,类似`log.SetFlag()` <details> <summary>main.go</summary> ``` package main import ( "fmt" ) const ( A = 1<<iota B C ) func demo(flag int) { if flag&A != 0 { fmt.Printf("%+v\n", "A") } if flag&B != 0 { fmt.Printf("%+v\n", "B") } if flag&C != 0 { fmt.Printf("%+v\n", "C") } } func main() { demo(A | C) } ``` </details> <br /> ### 指针 ``` func main() { i := 10 ip := &i fmt.Printf("原始指针的内存地址是:%p\n", &ip) //0xc042072018 modify(ip) fmt.Println("int值被修改了,新值为:", i) //1 } func modify(ip *int) { fmt.Printf("函数里接收到的指针的内存地址是:%p\n", &ip) //0xc042072028 *ip = 1 } ``` > [参考](https://studygolang.com/articles/19386?utm_source=tuicool&utm_medium=referral) 1. 指针指向的数据都是在堆上分配的 2. 值的拷贝是昂贵的,所以用一个指针来代替 3. 如果可以证明它里面没有指针,垃圾回收器会直接越过这块内存 > 垃圾回收器回收一个变量时,要检查该类型里是否有指针。 如果有,要检查指针所指向的内存是否可被回收,进而才能决定这个变量能否被回收。如此递归下去。 如果被回收的变量里面没有指针, 就不需要进去递归扫描了,直接回收掉就行 4. 如果结构体中没有指针,则可编译时知道,降低 gc 压力的压力 ### 条件编译 目录结构 ``` ├─echo ├─echo1.go └─echo2.go └─main.go ``` echo1.go ``` // +build !echo2 package echo func Echo() { fmt.Println("echo1.go") } ``` echo2.go ``` // +build echo2 package echo func Echo() { fmt.Println("echo1.go") } ``` main.go ``` func main() { echo.Echo() } ``` ``` go build -tags=echo2 main.go && main.exe //output echo2.go go build main.go && main.exe //output echo1.go ``` ### 中间件制作 以 http 接口为例子 ``` type minddleWarehandle struct { r *httprouter.Router } func NewMiddleWareHandle(r *httprouter.Router) minddleWarehandle { m := minddleWarehandle{r: r} return m } // !!! 覆写 ServerHttp 接口 func (m minddleWarehandle) ServerHttp(w http.ResponseWriter, r *http.Request) { //check session ValidateUserSession(r) m.r.ServeHTTP(w, r) } func RegisetHandlers() *httprouter.Router { router := httprouter.New() router.POST("/user", CreateUser) router.POST("/user/:user_name", Login) return router } func main() { r := RegisetHandlers() mh := NewMiddleWareHandle(r)// 添加一层中间件 http.ListenAndServe(":8000", mh.r) } ``` ### 传送二进文件,如视频等 ``` video, e := os.Open(filepath) defer video.Close() if e != nil { log.Println("error") return } //把文件放入响应中 w.Header().Set("Content-Type", "video/mp4") http.ServeContent(w, r, "", time.Now(), video) ``` ### Read(p []byte) (n int, err error) 函数获取全部数据 ``` //使用此函数 ioutil.ReadAll(r.Body.Read()) ``` ### 打印 log.print... 显示文件与行数 ``` log.SetFlags(log.Lshortfile | log.LstdFlags) // output 2012/07/24 19:27:55 X.cgo1.go:14: ... ``` ### 使用 close 关闭 chan 每当使用完chan后,需关闭 >[参考](https://blog.csdn.net/len_yue_mo_fu/article/details/80315799) 实例一: ``` func write(ch1 chan int) { for i := 0; i < 10; i++ { ch1<-i } close(ch1) //当复制完close 时,需要关闭 } ``` 实例二: ``` quit := make(chan bool) go func() { for { select { case <-quit: // closed channel 不会阻塞,因此可⽤作退出通知。 println("he") return default: // 执⾏正常任务。 println(time.Now().Nanosecond()) time.Sleep(time.Second) } } }() time.Sleep(2 * time.Second) close(quit) // 发出退出通知。 time.Sleep(2 * time.Second) ``` ### iota 创建 字节大小 ``` const ( _ = 1 << (10 * iota) KB // 1024 MB // 1048576 GB // 1073741824 TB // 1099511627776 (exceeds 1 << 32) PB // 1125899906842624 EB // 1152921504606846976 ZB // 1180591620717411303424 (exceeds 1 << 64) YB // 1208925819614629174706176 ) ``` ### 递归函数中传递切片 ``` func sliceModify(slice *[]int) { *slice = append(*slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(&slice) fmt.Println(slice) // [1 2 3 4 5 6] } ``` ### 自定义tag标签(reflect) ``` type Student struct { Name string `demo:"user_name"` Age int Score float32 `demo:"sc"` } func test(a interface{}) { v := reflect.TypeOf(a) numFiled := v.NumField() //获取filed 数量 for i := 0; i < numFiled; i++ { value, ok := v.Field(i).Tag.Lookup("demo") if ok { fmt.Println(value) // user_name sc } } } func main() { var a = Student{ Name: "stu01", Age: 18, Score: 92, } test(a) } ``` ### struct 的值模式与指针模式 > [参考](https://mp.weixin.qq.com/s/11wxINd_RA40YD0bL2Zbqg) 1. 数据分配密集型 选用值模式 2. 方法调用密集型(方法平凡调用) 选中指针模式 ### 整数转二进制 ``` var a int64 =1 fmt.Printf("%08b\n",a ) //00000001 整数转8位二进制 fmt.Printf("%010b\n",a ) //0000000001 整数转108位二进制 ``` ### chan struct{} 创建操作信号 struct{} 是内存最小单位,chan bool 次之,interface{}最差 ``` a:=make(chan struct{}) go func() { time.Sleep(1*time.Second) close(a) }() <-a ``` ### go func(v string){}(v) 在循环中需要传入参数 error ``` var url=[]string{"http://www.baidu.com","http://www.bilibili.com","http://www.baidu.com",} for _, v := range url { go func() { fmt.Printf("%+v\n", v) }() } time.Sleep(3*time.Second) //output http://www.baidu.com http://www.baidu.com http://www.baidu.com ``` good ``` var url=[]string{"http://www.baidu.com","http://www.bilibili.com","http://www.baidu.com",} for _, v := range url { go func(v string) { fmt.Printf("%+v\n", v) }(v) } time.Sleep(3*time.Second) //output http://www.baidu.com http://www.bilibili.com http://www.baidu.com ``` ### 退出多个循环 ``` func main() { L1: for x := 0; x < 3; x++ { L2: for y := 0; y < 5; y++ { if y > 2 { continue L2 } if x > 1 { break L1 } //退出多个循环,与 goto 类似 print(x, ":", y, " ") } println() } } //output //0:0 0:1 0:2 //1:0 1:1 1:2 ``` ### 跳出 for select 循环 1. return ``` for { select { case <-quit: //this.Close() return case msg := <-this.sendChan: if msg.QuictClient { this.quit<- struct{}{} } default: time.Sleep(1 * time.Second) } log.Print("1") } log.Print("2") ``` 2. break loop ``` LOOP:for { select { case <-quit: case msg := <-this.sendChan: break LOOP default: time.Sleep(1 * time.Second) } log.Print("1") } log.Print("2") ``` 3. goto ``` for { select { case <-quit: case msg := <-this.sendChan: goto LOOP default: time.Sleep(1 * time.Second) } log.Print("1") } LOOP: log.Print("2") ``` ## var Config sturct{} 存储配置文件 bad ``` type config struct{ name string } var Config config ``` good ``` var Config struct{ name string } ``` > 注意: 如果 sturct 有方法则不能使用此