??xml version="1.0" encoding="utf-8" standalone="yes"?>羞羞视频网站在线观看,中文字幕国产一区,2019中文字幕在线观看http://www.aygfsteel.com/stevenjohn/category/55274.html那些青春的岁?/description>zh-cnThu, 03 Aug 2017 05:37:30 GMTThu, 03 Aug 2017 05:37:30 GMT60GoLang之方法与接口http://www.aygfsteel.com/stevenjohn/archive/2017/08/03/432720.htmlabinabinThu, 03 Aug 2017 03:34:00 GMThttp://www.aygfsteel.com/stevenjohn/archive/2017/08/03/432720.htmlhttp://www.aygfsteel.com/stevenjohn/comments/432720.htmlhttp://www.aygfsteel.com/stevenjohn/archive/2017/08/03/432720.html#Feedback0http://www.aygfsteel.com/stevenjohn/comments/commentRss/432720.htmlhttp://www.aygfsteel.com/stevenjohn/services/trackbacks/432720.htmlGo语言没有沿袭传统面向对象~程中的诸多概念Q比如ѝ虚函数、构造函数和析构函数、隐藏的this指针{?/p>

 

Ҏ

Go 语言中同时有函数和方法?span style="color: #ff0000;">Ҏ是一个包含了接受者(receiverQ的函数Qreceiver可以是内|类型或者结构体cd的一个值或者是一个指针。所有给定类型的Ҏ属于该类型的Ҏ集?br />

如下面的q个例子Q定义了一个新cdIntegerQ它和int一P只是为它内置的intcd增加了个新方法Less()

复制代码
type Integer int   func (a Integer) Less(b Integer) bool {     return a < b  }  func main() {     var a Integer = 1       if a.Less(2) {         fmt.Println("less then 2")     }    }
复制代码

可以看出QGo语言在自定义cd的对象中没有C++/Java那种隐藏的this指针Q而是在定义成员方法时昑ּ声明了其所属的对象?/p>

 

method的语法如下:

func (r ReceiverType) funcName(parameters) (results)

当调用methodӞ会将receiver作ؓ函数的第一个参敎ͼ

funcName(r, parameters);

所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的|需要传递对象的指针?/p>

指针作ؓReceiver会对实例对象的内容发生操?而普通类型作为Receiver仅仅是以副本作ؓ操作对象,q不对原实例对象发生操作?/p>

复制代码
func (a *Ingeger) Add(b Integer) {     *a += b }  func main() {     var a Integer = 1      a.Add(3)     fmt.Println("a =", a)     //  a = 4 }
复制代码

如果AddҎ不用指针,则aq回的结果不变,q是因ؓGo语言函数的参C是基于g递?/p>

注意Q?span style="color: #ff0000;">当方法的接受者是指针Ӟ即用值类型调用那么方法内部也是对指针的操作?/span>

 

之前说过QGo语言没有构造函数的概念Q通常使用一个全局函数来完成。例如:

复制代码
func NewRect(x, y, width, height float64) *Rect {     return &Rect{x, y, width, height} }     func main() {     rect1 := NewRect(1,2,10,20)     fmt.Println(rect1.width) }
复制代码

 

 


匿名l合

Go语言提供了承,但是采用了组合的语法Q我们将其称为匿名组合,例如Q?/span>

复制代码
type Base struct {     name string }  func (base *Base) Set(myname string) {     base.name = myname }  func (base *Base) Get() string {     return base.name }  type Derived struct {     Base     age int  }  func (derived *Derived) Get() (nm string, ag int) {     return derived.name, derived.age }   func main() {     b := &Derived{}      b.Set("sina")     fmt.Println(b.Get()) }
复制代码

例子中,在Basecd定义了get()和set()两个ҎQ而Derivedcdl承了Basec,q改写了Get()ҎQ在Derived对象调用Set()ҎQ会加蝲基类对应的方法;而调用Get()ҎӞ加蝲zcL写的Ҏ?/p>

 

l合的类型和被组合的cd包含同名成员Ӟ 会不会有问题呢?可以参考下面的例子Q?/p>

复制代码
type Base struct {     name string     age int }  func (base *Base) Set(myname string, myage int) {     base.name = myname     base.age = myage }  type Derived struct {     Base     name string }  func main() {     b := &Derived{}      b.Set("sina", 30)     fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name)     fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age) }
复制代码

 

 

 


D义和引用语义

D义和引用语义的差别在于赋|比如

b = a b.Modify()

如果b的修改不会媄响a的|那么此类型属于值类型;如果会媄响a的|那么此类型是引用cd?/span>

Go语言中的大多数类型都ZD义,包括Q?/p>

  • 基本cdQ如byte、int、bool、float32、string{;
  • 复合cdQ如arry、struct、pointer{;

 

C语言中的数组比较特别Q通过函数传递一个数l的时候基于引用语义,但是在结构体定义数组变量的时候基于D义。而在Go语言中,数组和基本类型没有区别,是很Ua的值类型,例如Q?/p>

var a = [3] int{1,2,3} var b = a b[1]++ fmt.Println(a, b)   // [1 2 3] [1 3 3]

从结果看Qb=a赋D句是数组内容的完整复Ӟ要想表达引用Q需要用指针Q?/p>

var a = [3] int{1,2,3} var b = &a    // 引用语义 b[1]++ fmt.Println(a, b)   // [1 3 3] [1 3 3]

 

 


接口

Interface 是一l抽象方法(未具体实现的Ҏ/仅包含方法名参数q回值的ҎQ的集合Q如果实C interface 中的所有方法,卌c?对象实C该接口?/p>

Interface 的声明格式:

type interfaceName interface {       //Ҏ列表   }  

Interface 可以被Q意对象实玎ͼ一个类?对象也可以实现多?interfaceQ?br />interface的变量可以持有Q意实现该interfacecd的对象?/span>

 如下面的例子Q?/p>

复制代码
package main      import "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实现     // 因ؓq三个类型都实现了这两个Ҏ     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}          //定义Mencd的变量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)         //q三个都是不同类型的元素Q但是他们实Cinterface同一个接?/span>         x[0], x[1], x[2] = paul, sam, mike          for _, value := range x{             value.SayHi()         }     }
复制代码

 

I接?/h3>

Iinterface(interface{})不包含Q何的methodQ正因ؓ如此Q?span style="background-color: #ffff00;">所有的cd都实CIinterface。空interface对于描述起不CQ何的作用(因ؓ它不包含M的methodQ,但是Iinterface在我们需要存储Q意类型的数值的时候相当有用,因ؓ它可以存储Q意类型的数倹{它有点cM于C语言的void*cd?/span>

复制代码
// 定义a为空接口     var a interface{}     var i int = 5     s := "Hello world"     // a可以存储Lcd的数?/span>     a = i     a = s
复制代码

 

interface的变量里面可以存储Q意类型的数|该类型实CinterfaceQ,那么我们怎么反向知道q个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种ҎQswitch试、Comma-ok断言?/p>

 

switch试如下Q?/p>

复制代码
type Element interface{} type List [] Element  type 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)         }        }    }
复制代码

 

如果使用Comma-ok断言的话Q?/p>

复制代码
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)         }     } }
复制代码

 

 

嵌入接口

正如structcd可以包含一个匿名字D,interface也可以嵌套另外一个接口?/p>

如果一个interface1作ؓinterface2的一个嵌入字D,那么interface2隐式的包含了interface1里面的method?/p>

 

 

反射

所谓反(reflectQ就是能查程序在q行时的状态?/p>

使用reflect一般分成三步,下面要的讲解一下:要去反射是一个类型的?q些值都实现了空interface)Q首先需要把它{化成reflect对象(reflect.Type或者reflect.ValueQ根据不同的情况调用不同的函?。这两种获取方式如下Q?/p>

 t := reflect.TypeOf(i)    //得到cd的元数据,通过t我们能获取类型定义里面的所有元?/span>  v := reflect.ValueOf(i)   //得到实际的|通过v我们获取存储在里面的|q可以去改变?/span>

 

转化为reflect对象之后我们可以进行一些操作了Q也是reflect对象转化成相应的|例如

tag := t.Elem().Field(0).Tag  //获取定义在struct里面的标{?/span> name := v.Elem().Field(0).String()  //获取存储在第一个字D里面的?/span>

 

获取反射Dq回相应的类型和数?/p>

var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())

 

最后,反射的话Q那么反的字段必须是可修改的,我们前面学习q传值和传引用,q个里面也是一L道理。反的字段必须是可d的意思是Q如果下面这样写Q那么会发生错误

var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1)

 

如果要修改相应的|必须q样?/p>

var x float64 = 3.4 p := reflect.ValueOf(&x) v := p.Elem() v.SetFloat(7.1)

上面只是对反的单介l,更深入的理解q需要自己在~程中不断的实践?/p>

 

 

参考文档:

http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/

http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/

 http://www.cnblogs.com/chenny7/p/4497969.html







abin 2017-08-03 11:34 发表评论
]]>
老虞要学GoLang-函数(?http://www.aygfsteel.com/stevenjohn/archive/2017/08/02/432718.htmlabinabinWed, 02 Aug 2017 08:39:00 GMThttp://www.aygfsteel.com/stevenjohn/archive/2017/08/02/432718.htmlhttp://www.aygfsteel.com/stevenjohn/comments/432718.htmlhttp://www.aygfsteel.com/stevenjohn/archive/2017/08/02/432718.html#Feedback0http://www.aygfsteel.com/stevenjohn/comments/commentRss/432718.htmlhttp://www.aygfsteel.com/stevenjohn/services/trackbacks/432718.html不可或缺的函敎ͼ在Go中定义函数的方式如下Q?/p>
func (p myType ) funcName ( a, b int , c string ) ( r , s int ) {     return } 

通过函数定义Q我们可以看到Go中函数和其他语言中的共性和Ҏ?/p>

共?/h3>
  • 关键?#8212;—func
  • Ҏ?#8212;—funcName
  • 入参——— a,b int,b string
  • q回?#8212;— r,s int
  • 函数?#8212;— {}

Ҏ?/h3>

Go中函数的Ҏ是非常LQ给我们带来不一L~程体验?/p>

为特定类型定义函敎ͼ即ؓcd对象定义Ҏ

在Go中通过l函数标明所属类型,来给该类型定义方法,上面?nbsp;p myType 卌C给myType声明了一个方法, p myType 不是必须的。如果没有,则纯_Ҏ一个函敎ͼ通过包名U访问。packageName.funcationName

如:

//定义新的cddoubleQ主要目的是lfloat64cd扩充Ҏ type double float64  //判断a是否{于b func (a double) IsEqual(b double) bool {     var r = a - b     if r == 0.0 {         return true     } else if r < 0.0 {         return r > -0.0001     }     return r < 0.0001 }  //判断a是否{于b func IsEqual(a, b float64) bool {     var r = a - b     if r == 0.0 {         return true     } else if r < 0.0 {         return r > -0.0001     }     return r < 0.0001 }  func main() {     var a double = 1.999999     var b double = 1.9999998     fmt.Println(a.IsEqual(b))     fmt.Println(a.IsEqual(3))     fmt.Println( IsEqual( (float64)(a), (float64)(b) ) )  } 

上述CZ?float64 基本cd扩充了方法IsEqualQ该Ҏ主要是解决精度问题?其方法调用方式ؓQ?nbsp;a.IsEqual(double) Q如果不扩充ҎQ我们只能用函?code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; white-space: nowrap; font-family: monospace, Monaco;">IsEqual(a, b float64)

入参中,如果q箋的参数类型一_则可以省略连l多个参数的cdQ只保留最后一个类型声明?/h4>

?nbsp;func IsEqual(a, b float64) bool q个Ҏ只保留了一个类型声?此时入参a和b均是float64数据cd?q样也是可以的: func IsEqual(a, b float64, accuracy int) bool

变参Q入参支持变?卛_接受不确定数量的同一cd的参?/h4>

?nbsp;func Sum(args ...int) 参数args是的sliceQ其元素cd为int 。经怋用的fmt.Printf是一个接受Q意个数参数的函数 fmt.Printf(format string, args ...interface{})

支持多返回?/h4>

前面我们定义函数时返回值有两个r,s 。这是非常有用的Q我在写C#代码Ӟ常常Z从已有函C获得更多的信息,需要修改函数签名,使用out ,ref {方式去获得更多q回l果。而现在用Go时则很简单,直接在返回值后面添加返回参数即可?/p>

?在C#中一个字W串转换为intcd旉辑代码

int v=0;  if ( int.TryPase("123456",out v) ) {     //code } 

而在Go中,则可以这样实?逻辑_而明?/p>

if v,isOk :=int.TryPase("123456") ; isOk {     //code } 

同时在Go中很多函数充分利用了多返回?/p>

  • func (file *File) Write(b []byte) (n int, err error)
  • func Sincos(x float64) (sin, cos float64)

那么如果我只需要某一个返回|而不兛_其他q回值的话,我该如何办呢Q?q时可以单的使用W号下划U?#8221;_“ 来忽略不兛_的返回倹{如Q?/p>

_, cos = math.Sincos(3.1415) //只需要cos计算的?

命名q回?/h4>

前面我们说了函数可以有多个返回|q里我还要说的是Q在函数定义时可以给所有的q回值分别命名,q样p在函CL位置l不同返回值复Ӟ而不需要在return语句中才指定q回倹{同时也能增强可L,也提高godoc所生成文档的可L?/p>

如果不支持命名返回|我可能会是这样做?/p>

func ReadFull(r Reader, buf []byte) (int, error) {     var n int     var err error      for len(buf) > 0  {         var nr int         nr, err = r.Read(buf)          n += nr         if err !=nil {             return n,err         }         buf = buf[nr:]     }     return n,err } 

但支持给q回值命名后Q实际上是省略了变量的声明Qreturn时无需写成return n,err 而是直接将D?/p>

func ReadFull(r Reader, buf []byte) (n int, err error) {     for len(buf) > 0 && err == nil {         var nr int         nr, err = r.Read(buf)         n += nr         buf = buf[nr:]     }     return } 

函数也是“?#8221;

和Go中其他东西一P函数也是|q样可以声明一个函数类型的变量Q将函数作ؓ参数传递?/p>

声明函数为值的变量(匿名函数:可赋g变量Q也可直接执?

//赋?fc := func(msg string) {     fmt.Println("you say :", msg) } fmt.Printf("%T \n", fc) fc("hello,my love") //直接执行 func(msg string) {     fmt.Println("say :", msg) }("I love to code") 

输出l果如下Q这里表明fc 的类型ؓQfunc(string)

func(string)  you say : hello,my love say : I love to code 

函C为入参(回调函数Q,能带来便利。如日志处理Qؓ了统一处理Q将信息均通过指定函数去记录日志,且是否记录日志还有开?/p>

func Log(title string, getMsg func() string) {     //如果开启日志记?则记录日?    if true {         fmt.Println(title, ":", getMsg())     } } //---------调用-------------- count := 0 msg := func() string {     count++     return "您没有即使提醒我,已触犯法? } Log("error", msg) Log("warring", msg) Log("info", msg) fmt.Println(count) 

q里输出l果如下Qcount 也发生了变化

error : 您没有即使提醒我,已触犯法?warring : 您没有即使提醒我,已触犯法?info : 您没有即使提醒我,已触犯法?3 

函数也是“cd”

你有没有注意C面示例中?nbsp;fc := func(msg string)... Q既然匿名函数可以赋值给一个变量,同时我们l常q样lint赋?nbsp;value := 2 ,是否我们可以声明func(string) cd 呢,当然是可以的?/p>

//一个记录日志的cdQfunc(string) type saveLog func(msg string)  //字W串转换为int64,如果转换p|调用saveLog func stringToInt(s string, log saveLog) int64 {      if value, err := strconv.ParseInt(s, 0, 0); err != nil {         log(err.Error())         return 0     } else {         return value     } }  //记录日志消息的具体实?func myLog(msg string) {     fmt.Println("Find Error:", msg) }  func main() {     stringToInt("123", myLog) //转换时将调用mylog记录日志     stringToInt("s", myLog) } 

q里我们定义了一个类型,专门用作记录日志的标准接口。在stringToInt函数中如果{换失败则调用我自己定义的接口函数q行日志处理Q至于最l执行的哪个函数Q则无需兛_?/p>

defer 延迟函数

defer 又是一个创斎ͼ它的作用是:延迟执行Q在声明时不会立x行,而是在函数return后时按照后进先出的原则依ơ执行每一个defer。这样带来的好处是,能确保我们定义的函数能百分之百能够被执行刎ͼq样p做很多我们想做的事,如释放资源,清理数据Q记录日志等

q里我们重点来说明下defer的执行顺?/p>

func deferFunc() int {     index := 0      fc := func() {          fmt.Println(index, "匿名函数1")         index++          defer func() {             fmt.Println(index, "匿名函数1-1")             index++         }()     }      defer func() {         fmt.Println(index, "匿名函数2")         index++     }()      defer fc()      return func() int {         fmt.Println(index, "匿名函数3")         index++         return index     }() }  func main() {     deferFunc() } 

q里输出l果如下Q?/p>

0 匿名函数3 1 匿名函数1 2 匿名函数1-1 3 匿名函数2 

有如下结论:

  • defer 是在执行完return 后执?/li>
  • defer 后进先执?/li>

另外Q我们常使用deferd闭IO,在正常打开文g后,qd明一个deferQ这样就不会忘记关闭文gQ也能保证在出现异常{不可预料的情况下也能关闭文件。而不像其他语aQ?code style="margin: 0px 2px; padding: 0px 5px; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; white-space: nowrap; font-family: monospace, Monaco;">try-catch 或?nbsp;using() 方式q行处理?/p>

file , err :=os.Open(file) if err != nil {     return err } defer file.Close()  //dosomething with file 

后箋Q我讨论: 作用域、传值和传指?以及 保留函数init(),main()

本笔C所写代码存储位|:



abin 2017-08-02 16:39 发表评论
]]>
վ֩ģ壺 | | ʡ| | ʡ| | Ϫ| | | Ľ| | ɽ| | | | | | | Ϫ| | | | ̷| | | ͨ| пǰ| | | | ƽɽ| ϲ| ʤ| Ԫ| | | ī񹤿| ɽ| | ͼ| |