Go语言

GO语言语法

2020-01-01  本文已影响0人  潘晓华Michael
Go语言语法

顺序编程

变量

var v10 int
v10 = 123

v11 := 234

// 匿名变量示例
func GetName() (firstName, lastName nickName string) {
  return "May", "Chan", "Chibi"
}
_, _, nickName := GetName()

常量

iota是一个特殊的常量,它会在每一个const关键字出现时重置为0,然后在下一个const出现前,每出现一次iota,它代表的值加1。如果两个const的赋值语句表达式一样,后一个赋值可省略。

const (
  c0 = iota
  c1 = iota
  c2
)

枚举

const (
  Sunday = iota
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  numberOfDays
)

类型

// 切片例子
mySlice := make([]int, 5, 10)

fmt.Println("len(mySlice):", len(mySlice))
fmt.Println("cap(mySlice):", cap(mySlice))

mySlice = append(mySlice, 1, 2, 3)

mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2)

slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}

copy(slice2, slice1) //将slice1中的前3个元素复制到slice2
copy(slice1, slice2) //将slice2中的前3个元素复制到slice1
// Map例子
package main
import (
    "fmt"
)

type PersonInfo struct{
    ID string
    Name string
    Address string
}

func main(){
    var personDB map[string] PersonInfo
    personDB = make(map[string] PersonInfo)

    personDB["12345"] = PersonInfo{"12345", "Tom", "Room 12345"}
    personDB["546"] = PersonInfo{"546", "Tom", "Room 546"}

    person, ok := personDB["1234"]

    if ok {
        fmt.Println("Found person", person.Name)
    }else{
        fmt.Println("Did not find person")
    }
    delete(personDB, "546")
    fmt.Println(personDB["12345"].Name)
}

流程控制

// if else
func if_else(x int) int {
    if x == 0 {
        return 5
    }else{
        return x
    }
}

// switch case
func switch_case(i int){ //无需break
    switch i{
    case 0:
        fmt.Println("0")
    case 1:
        fmt.Println("1")
    default:
        fmt.Println("Default")
    }
}

// for
sum := 0
for {
    sum ++
    if sum > 10 {
        break
    }
}

for j := 0; j < 5; j++ {
    ftm.Println(j)
}

// goto
func myfunc(){
    i := 0
    HERE:
    fmt.Println(i)
    i++
    if i < 10 {
        goto HERE
    }
}

函数

import "mymath"  //假设Add被放在一个叫mymath的包中
c := mymath.Add(1, 2)

小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。

func myfunc(args ...int) {
  for _, arg := range args {
    fmt.Println(arg)
} 
}

func myfunc(args ...int) {
    for _, arg := range args {
        fmt.Println(arg)
    }
    myfunc2(args...)
}

任意类型的不定参数

func Printf(format string, args ...interface{}){
    for _, arg := range args {
        switch arg.(type) {
            case int:
                fmt.Println(arg, "is an int value")
            case string:
                fmt.Println(arg, "is a string value")
            case int64:
                fmt.Println(arg, "is an int64 value")
            default:
                fmt.Println(arg, "is an unknown type")
        }
    }
}

多返回值

func (file *File) Read(b []byte) (n int, err Error)

匿名函数与闭包

package main
import(
    "fmt"
)
func main(){
    var j int = 5
    a := func()(func()){
        var i int = 10
        return func() {
            fmt.Printf("i, j: %d, %d\n", i, j)
        }
    }()
    a()
    j *= 2
    a()
}
// 结果
// i, j: 10, 5
// i, j: 10, 10

异常处理

type error interface {
  Error() string
}

大多数函数,如果要返回错误,可以定义如下模式。

func Foo(param int)(n int, err error) {
  // ...
}

n, err := Foo(0)
if err != nil {
  // 错误处理
} else {
  // 使用返回值n
}

type PathError struct {
  Op string
  Path string
  Err  error
}
func (e *PathError) Error() string {
  return e.Op + " " + e.Path + ": " + e.Err.Error()
}

defer

func CopyFile(dst, src string) (w int64, err error){
    srcFile, err := os.Open(src)
    if err != nil {
        return
    }
    defer srcFile.Close()

    dstFile, err := os.Create(dst)
    if err != nil {
        return
    }
    defer dstFile.Close()

    return io.Copy(dstFile, srcFile)
}

panic/recover

defer func() {
    if r := recover(); r != nil {
        log.Printf("Runtime error caught: %v", r)
    }
}()

面向对象编程

结构体

type Integer int
func (a Integer) Less(b Integer) bool {
  return a < b
}
type Header map[string] []string
func (h Header) Add(key, value string){
  textproto.MIMEHeader(h).Add(key, value)
}
func (h Header) Set(key, value string){
  textproto.MIMEHeader(h).Set(key, value)
}

引用类型:数组切片、map、channel、接口(interface)
值类型:byte、int、bool、float32、float64、string、array、struct、pointer等

type Animal struct {
    Name   string  //名称
    Color  string  //颜色
    Height float32 //身高
    Weight float32 //体重
    Age    int     //年龄
}
//奔跑
func (a Animal)Run() {
    fmt.Println(a.Name + "is running")
}
type Lion struct {
    Animal //匿名字段
}

func main(){
    var lion = Lion{
        Animal{
            Name:  "小狮子",
            Color: "灰色",
        },
    }
    lion.Run()
    fmt.Println(lion.Name)
}

匿名组合相当于以其类型名称(去掉包名部分)作为成员变量的名字。
Go语言中,要使某个符号对其他包可见,需要将该符号定义为以大写字母开头。Go语言中符号的可访问性是包一级别,而不是类型级别。

接口

Go语言中,一个类只需要实现了接口要求的所有函数,就说这个类实现了该接口。

type File struct {
  // ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Close() error

type IFile interface{
  IReader
  IWrite
  IClose
}
type IReader interface{
  Read(buf []byte) (n int, err error)
}
type IWriter interface{
  Write(buf []byte) (n int, err error)
}
type IClose interface{
  Close() error
}

var file1 IFile = new(File)
var file2 IReader = new(File)

Go语言中任何对象实例都满足空接口interface{}。

并发编程

go channel

package main
import "fmt"

func Count(ch chan int){
    fmt.Println("Counting")
    ch <- 1
}

func main(){
    chs := make([]chan int, 10)
    for i :=0; i < 10; i++ {
        chs[i] = make(chan int)
        go Count(chs[i])
    }

    for _, ch := range(chs) {
        a := <- ch
        fmt.Println(a)
    }
}

超时机制

timeout := make(chan bool, 1)
go func(){
    time.Sleep(1e9)
    timeout <- true
}()

select {
case <-ch:
    //从ch中读取到数据
case <-timeout
    //一直没有从ch中读取到数据,但从timeout中读取到了数据
}

channel的传递


type PipeData struct {
    value int
    handler func(int) int
    next chan int
}

func handle(queue chan *PipeData){
    for data := range queue{
        data.next <- data.handler(data.value)
    }
}

关闭channel

close(ch)

同步锁

var l sync.Mutex
func foo(){
    l.Lock()
    defer l.Unlock()
    //...
}

全局唯一操作

var a string
var once sync.once
func setup(){
    a = "hello"
}

func doprint() {
    once.Do(setup)
    print(a)
}

func twoprint(){
    go doprint()
    go doprint()
}

以上代码setup只会运行一次。

网络编程

Socket编程

Dial():连接,支持如下协议:tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6

conn, err := net.Dial("tcp", "192.168.0.2:2100")
conn2, err := net.Dial("udp", "192.168.0.2:53")
conn3, err := net.Dial("ip4:icmp", "www.baidu.com")

Write()与Read()方法来发送数据与接收数据。

HTTP编程

net/http包,包含HTTP客户端与服务端的具体实现。

resp, err := http.Get("http://example.com/")
if err != nil{
    return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)
resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil{
    return
}
if resp.StatusCode != http.StatusOK{
    return
}
resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"artical body"}})
if err != nil{
    return
}
resp, err := http.Head("http://example.com/")
req, err := http.NewRequest("GET", "http://example.com/posts", nil)
req.Header.Add("User-Agent", "Gobook User-Agent")

client := &http.Client{...}
resp, err := client.Do(req)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){
  fmt.Fprintf(w, "hello")
})

RPC

net/rpc,包实现RPC协议的细节

func (t *T) MethodName(argType T1, replyType *T2) error

T1与T2默认会使用encoding/gob包进行编码与解码。
argType表示由RPC客户端传入的参数
replyType表示要返回给RPC客户端的结果
服务端示例

package main
import (
    "log"
    "net"
    "net/http"
    "net/rpc"
)
type Args struct{
    A, B int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error{
    *reply = args.A * args.B
    return nil
}

func main(){
    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1234")
    if e != nil{
        log.Fatal("listen error", e)
    }
    http.Serve(l, nil)
}

客户端示例

package main
import (
    "net/rpc"
    "fmt"
    "log"
)

type Args struct{
    A, B int
}

func main(){
// 顺序执行RPC
    client, _ := rpc.DialHTTP("tcp", "127.0.0.1:1234")
    args := &Args{7,8}
    var reply int
    err := client.Call("Arith.Multiply", args, &reply)
    if err != nil{
        log.Fatal(err)
    }
    fmt.Println(reply)

//异步执行RPC
        var reply2 int
    divCall := client.Go("Arith.Multiply", &Args{6,8}, &reply2, nil)
    divCall = <-divCall.Done
    fmt.Println(reply2)
}

JSON

encoding/json标准库
json.Marshal(),将一组数据进行JSON格式化编码
json.Unmarshal(),将JSON格式的文档解码为Go里边预期的数据结构。

data := make(map[string] string)
data["name"] = "Michael"
data2, err := json.Marshal(data)
if err != nil{
    fmt.Println("err")
    return
}
fmt.Println(string(data2[:]))
data := make(map[string] string)
data_json := []byte("{\"name\":\"Michael\"}")

json.Unmarshal(data_json, &data)
fmt.Println(data)

反射

type T struct{
    A int
    B string
}
t := T{203, "mh203"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()

for i := 0; i < s.NumField(); i++ {
    f := s.Field(i)
    fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}

Go工具

包文档查看(网站形式)

$ godoc -http=:8080
$ godoc -http=:8080 -path="."

包文档查看(命令行形式)

$ go doc foo

代码格式化

$ go fmt hello.go
$ gofmt -l -w .

项目构建
go build:用于测试编译包,在项目目录下生成可执行文件(有main包);不能生成包文件
go install:主要用来生成库和工具。一是编译包文件(无main包),将编译后的包文件放到 pkg 目录下($GOPATH/pkg)。二是编译生成可执行文件(有main包),将可执行文件放到 bin 目录($GOPATH/bin);生成可执行文件在当前目录下, go install 生成可执行文件在bin目录下($GOPATH/bin)

$ GOOS=linux GOARCH=amd64 go build calc 
$ go install calc

GOOS:系统,linux/darwin/windows
GOARCH:386/amd64

单元测试

$ go test calc

模块管理go mod

$ go mod download  # 下载指定模块
$ go mod init test.com/go  #初始化当前模块
$ go mod vendor       # 将依赖复制到当前vendor目录下

使用go module后,项目代码不需要放在GOPATH目录下。import项目的package时使用module路径。

以下是beego应用使用module来管理依赖的例子

$ cat <<EOF > go.mod
module beegoapp

go 1.12

replace (
    golang.org/x/crypto => github.com/golang/crypto v0.0.0-20191227163750-53104e6ec876
    golang.org/x/mod => github.com/golang/mod v0.1.0
    golang.org/x/net => github.com/golang/net v0.0.0-20191209160850-c0dbc17a3553
    golang.org/x/sync => github.com/golang/sync v0.0.0-20190911185100-cd5d95a43a6e
    golang.org/x/sys => github.com/golang/sys v0.0.0-20191228213918-04cbcbbfeed8
    golang.org/x/text => github.com/golang/text v0.3.2
    golang.org/x/tools => github.com/golang/tools v0.0.0-20191230220329-2aa90c603ae3
    golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20191204190536-9bdfabe68543
)

require (
    github.com/astaxie/beego v1.12.0
    github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
    github.com/smartystreets/goconvey v1.6.4
)

EOF

GO的GC规则

上一篇下一篇

猜你喜欢

热点阅读