一、go语言基础流程控制,语法,反射特性--go语言笔记

来源:互联网 发布:淘宝联盟软件下载 编辑:程序博客网 时间:2024/06/05 10:43

for循环

sum := 1for ; sum < 1000;  {    sum += sum}for sum<1000{    sum ++}

在第5行中,我们把很多值聚合在了一个case里面,同时,Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。

integer := 6switch integer {case 4:    fmt.Println("The integer was <= 4")    fallthroughcase 5:    fmt.Println("The integer was <= 5")    fallthroughcase 6:    fmt.Println("The integer was <= 6")    fallthroughcase 7:    fmt.Println("The integer was <= 7")    fallthroughcase 8:    fmt.Println("The integer was <= 8")    fallthroughdefault:    fmt.Println("default case")}

自动调用init->main

我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:

import(    "fmt")

然后我们代码里面可以通过如下的方式调用

fmt.Println(“hello world”)
上面这个fmt是Go语言的标准库,其实是去GOROOT环境变量指定目录下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:

相对路径

import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import绝对路径import “shorturl/model” //加载gopath/src/shorturl/model模块

上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事

点操作

我们有时候会看到如下的方式导入包

import(    . "fmt")

这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello world”)可以省略的写成Println(“hello world”)

别名操作

别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字

import(    f "fmt")

别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println(“hello world”)

_操作

这个操作经常是让很多人费解的一个操作符,请看下面这个import

import (    "database/sql"    _ "github.com/ziutek/mymysql/godrv")

_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。

interface

package mainimport "fmt"type Human struct {    name string    age int    phone string}type Student struct {    Human //匿名字段    school string    loan float32}type Employee struct {    Human //匿名字段    company string    money float32}//Human实现SayHi方法func (h Human) SayHi() {    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)}//Human实现Sing方法func (h Human) Sing(lyrics string) {    fmt.Println("La la la la...", lyrics)}//Employee重载Human的SayHi方法func (e Employee) SayHi() {    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,        e.company, e.phone)    }// Interface Men被Human,Student和Employee实现// 因为这三个类型都实现了这两个方法type Men interface {    SayHi()    Sing(lyrics string)}func main() {    mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}    paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}    sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}    tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}    //定义Men类型的变量i    var i Men    //i能存储Student    i = mike    fmt.Println("This is Mike, a Student:")    i.SayHi()    i.Sing("November rain")    //i也能存储Employee    i = tom    fmt.Println("This is tom, an Employee:")    i.SayHi()    i.Sing("Born to be wild")    //定义了slice Men    fmt.Println("Let's use a slice of Men and see what happens")    x := make([]Men, 3)    //这三个都是不同类型的元素,但是他们实现了interface同一个接口    x[0], x[1], x[2] = paul, sam, mike    for _, value := range x{        value.SayHi()    }}

通过上面的代码,你会发现interface就是一组抽象方法的集合,它必须由其他非interface类型实现,而不能自我实现, Go通过interface实现了duck-typing:即”当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子”。

空interface

空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

// 定义a为空接口var a interface{}var i int = 5s := "Hello world"// a可以存储任意类型的数值a = ia = s

一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!

interface的变量可以持有任意实现该interface类型的对象,这给我们编写函数(包括method)提供了一些额外的思考,我们是不是可以通过定义interface参数,让函数接受各种类型的参数。

举个例子:fmt.Println是我们常用的一个函数,但是你是否注意到它可以接受任意类型的数据。打开fmt的源码文件,你会看到这样一个定义:

type Stringer interface {
String() string
}
也就是说,任何实现了String方法的类型都能作为参数被fmt.Println调用,让我们来试一试

package mainimport (    "fmt"    "strconv")type Human struct {    name string    age int    phone string}// 通过这个方法 Human 实现了 fmt.Stringerfunc (h Human) String() string {    return "❰"+h.name+" - "+strconv.Itoa(h.age)+" years -  ✆ " +h.phone+"❱"}func main() {    Bob := Human{"Bob", 39, "000-7777-XXX"}    fmt.Println("This Human is : ", Bob)}

现在我们再回顾一下前面的Box示例,你会发现Color结构也定义了一个method:String。其实这也是实现了fmt.Stringer这个interface,即如果需要某个类型能被fmt包以特殊的格式输出,你就必须实现Stringer这个接口。如果没有实现这个接口,fmt将以默认的方式输出。

//实现同样的功能

fmt.Println("The biggest one is", boxes.BiggestsColor().String())fmt.Println("The biggest one is", boxes.BiggestsColor())

注:实现了error接口的对象(即实现了Error() string的对象),使用fmt输出时,会调用Error()方法,因此不必再定义String()方法了。

我们知道interface的变量里面可以存储任意类型的数值(该类型实现了interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:

Comma-ok断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。

让我们通过一个例子来更加深入的理解。

package main

import (
“fmt”
“strconv”
)

type Element interface{}
type List [] Element

type Person struct {
name string
age int
}

//定义了String方法,实现了fmt.Stringer
func (p Person) String() string {
return “(name: ” + p.name + ” - age: “+strconv.Itoa(p.age)+ ” years)”
}

func main() {
list := make(List, 3)
list[0] = 1 // an int
list[1] = “Hello” // a string
list[2] = Person{“Dennis”, 70}

for index, element := range list {    if value, ok := element.(int); ok {        fmt.Printf("list[%d] is an int and its value is %d\n", index, value)    } else if value, ok := element.(string); ok {        fmt.Printf("list[%d] is a string and its value is %s\n", index, value)    } else if value, ok := element.(Person); ok {        fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)    } else {        fmt.Printf("list[%d] is of a different type\n", index)    }}

}
是不是很简单啊,同时你是否注意到了多个if里面,还记得我前面介绍流程时讲过,if里面允许初始化变量。

也许你注意到了,我们断言的类型越多,那么if else也就越多,所以才引出了下面要介绍的switch。

switch测试

最好的讲解就是代码例子,现在让我们重写上面的这个实现

package mainimport (    "fmt"    "strconv")type Element interface{}type List [] Elementtype Person struct {    name string    age int}//打印func (p Person) String() string {    return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"}func main() {    list := make(List, 3)    list[0] = 1 //an int    list[1] = "Hello" //a string    list[2] = Person{"Dennis", 70}    for index, element := range list{        switch value := element.(type) {            case int:                fmt.Printf("list[%d] is an int and its value is %d\n", index, value)            case string:                fmt.Printf("list[%d] is a string and its value is %s\n", index, value)            case Person:                fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)            default:                fmt.Println("list[%d] is of a different type", index)        }    }}

这里有一点需要强调的是:element.(type)语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用comma-ok。

反射

having a rough time working with struct fields using reflect package. in particular, have not figured out how to set the field value.type t struct { fi int; fs string }var r t = t{ 123, "jblow" }var i64 int64 = 456getting Name of field i - this seems to workvar field = reflect.TypeOf(r).Field(i).Namegetting value of field i as a) interface{}, b) int - this seems to workvar iface interface{} = reflect.ValueOf(r).Field(i).Interface()var i int = int(reflect.ValueOf(r).Field(i).Int())setting value of field i - try one - panicreflect.ValueOf(r).Field(i).SetInt( i64 )panic: reflect.Value·SetInt using value obtained using unexported fieldassuming it did not like field names "id" and "name", so renamed to "Id" and "Name"a) is this assumption correct?b) if correct, thought not necessary since in same file / packagesetting value of field i - try two (with field names capitalized ) - panicreflect.ValueOf(r).Field(i).SetInt( 465 )reflect.ValueOf(r).Field(i).SetInt( i64 )panic: reflect.Value·SetInt using unaddressable valueFour. this works:reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )he documents as well that the field names must be exportable (begin with capital letter)
package mainimport (    "fmt"    "reflect")type Human struct {    name  string    age   int    phone string}type Student struct {    Human  //匿名字段    school string}type Employee struct {    Human   //匿名字段    company string}//Human定义methodfunc (h *Human) SayHi() {    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)}//Employee的method重写Human的methodfunc (e *Employee) SayHi() {    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,        e.company, e.phone) //Yes you can split into 2 lines here.}type t struct {    fi int    fs string}func main() {    //mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}    //sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}    /*        var i = 10        //markT := reflect.TypeOf(mark)        t := reflect.TypeOf(i)        v := reflect.ValueOf(i)        tag := t.Elem().Field(0).Tag        name := v.Elem().Field(0).String()        //fmt.Println("markT", markT)        fmt.Println("mark")        fmt.Println("tag:", tag)        fmt.Println("name:", name)    */    var r t = t{123, "jblow"}    //var i64 int64 = 456    var field = reflect.TypeOf(r).Field(0).Name    //reflect.ValueOf(r).Field(0).SetInt(i64)    fmt.Println(field)    fmt.Println(r)    var iface interface{} = reflect.ValueOf(r).Field(0).Interface()    var i int = int(reflect.ValueOf(r).Field(1).Int())    fmt.Println(iface)    fmt.Println(i)    /*            panic: reflect.Value.Interface: cannot return value obtained from unexported field or method            this works:        reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )        he documents as well that the field names must be exportable (begin with capital letter) 大写才行    */}
package mainimport (    "fmt"    "reflect")type Foo struct {    FirstName string `tag_name:"tag 1"`    LastName  string `tag_name:"tag 2"`    Age       int    `tag_name:"tag 3"`}func (f *Foo) reflect() {    val := reflect.ValueOf(f).Elem()    for i := 0; i < val.NumField(); i++ {        valueField := val.Field(i)        typeField := val.Type().Field(i)        tag := typeField.Tag        fmt.Printf("Field Name: %s,\t Field Value: %v,\t Tag Value: %s\n", typeField.Name, valueField.Interface(), tag.Get("tag_name"))    }}func main() {    f := &Foo{        FirstName: "Drew",        LastName:  "Olson",        Age:       30,    }    f.reflect()}
0 0
原创粉丝点击