goLang 之 type Method Value 和Method Expressions

在使用
goLang时,经常遇到
Method Value
Method Expressions的问题,简单记录一下两者的使用区别

goLangtype类型方法定义如下:

   func (p myType) funcName(q type) (r,s type){return 0,0) //类似这样

本质上这就是一种语法糖,方法调用如下:


instance.method(args) ---> (type).func(instance,args)

instance就是Receiver. 左边的称为Method value.右边的则是Method Expression.其实goLang 是推荐使用左边形式的,因为比较好理解。Method Value是包装后的状态对象,总是与特定的对象实例关联在一起,而Method Expression函数将Receiver作为第一个显示参数,调用时需手动传递。二者本质没有什么区别

只是Method value看起来更像面向对象的格式,且编译器会自动进行类型转换。 Method Expression更直观,更底层,编译器不会进行优化,会按照实际表达意义去执行。

Method Value赋值给变量,Receiver实例立即被复制,这个和goLang的一贯作风是一样的。只进行值传递,即使是传递指针,也只是传递指针的拷贝,只是拷贝的指针和原指针都指向一个地方,看起来好像是直接操作原数据

instance.method(args):可以用 value(值)pointer(指针) 变量调用所有绑定的任何方法,编译器会自动进行类型转换,转换后的效果和method的定义语义一致,和调用者的样式没有关系。


method定义的
Receiver
value形的,即使使用
pointer实例变量调用该方法,也是
value值拷贝;

method定义的Receiverpointer形的,即使使用value 实例变量调用该方法,也能改变Receiver实例状态。



package main  
  
import (  
        "fmt"  
)  
  
  
type Person struct {  
        Age int  
        Name string  
}  
  
  
func (this Person) Getage() int{  
    return  this.Age      
}  
  
func (this *Person) Setage(i int){  
    this.Age =  i  
}  
  
func main(){  
        //初始化s1,s2  
        s1:= Person{Age:12, Name: "tata"}  
        s2:= &Person{Age:100, Name: "tt"}     
        fmt.Println("s1=",s1)  
        fmt.Println("s2=",s2)  
        fmt.Println("------------------------")  
  
        //可以用 value 或 pointer 调用所有绑定的方法,编译器自动进行类型转换(按照method Reciever 的类型是value还是pointer进行转换,和调用者调用表现形式没有任何关系,调用执行结果按照method的定义语义进行解释)  
  
        fmt.Println("s1.Age=",s1.Getage())  
        fmt.Println("s2.Age=",s2.Getage())  
        fmt.Println("s2.Age=",(*s2).Getage())  
        s1.Setage(110)  
        s2.Setage(101)  
        fmt.Println("s1=",s1)  
        fmt.Println("s1=",s2)  
        (&s1).Setage(220)  
        fmt.Println("s1=",s1)  
        fmt.Println("------------------------")  
          
//如下是Method Value形式引用,其等价于Reciever直接调用,规则和直接调用完全一致  
        f := s1.Getage  
        s1.Setage(330)  
        fmt.Println("s1=",s1)  
        fmt.Println("s1.copy.Getage()=",f())  
        fmt.Println("s1=",s1)  
  
//如下四种方式也是Method Values模式,编译器进行自动转换,任何形式都可以调用成功   
        fmt.Println("------------------------")  
        sw := (&s1).Setage  
        f2 := (&s1).Getage  
        sw(880)  
        fmt.Println("s1=",s1)  
        fmt.Println("s1.copy.Getage()=",f2())  
        fmt.Println("s1=",s1)  
  
        ss := s1.Setage  
        f3 := s1.Getage  
        ss(990)  
        fmt.Println("s1=",s1)  
        fmt.Println("s1.copy.Getage()=",f3())  
        fmt.Println("s1=",s1)  
  
        se := s2.Setage  
        f4 := s2.Getage  
        se(110)  
        fmt.Println("s2=",s2)  
        fmt.Println("s2.copy.Getage()=",f4())  
        fmt.Println("s2=",s2)  
  
        se2 := (*s2).Setage  
        f5 := (*s2).Getage  
        se2(220)  
        fmt.Println("s2=",s2)  
        fmt.Println("s2.copy.Getage()=",f5())  
        fmt.Println("s2=",s2)  
  
//如下是Method Expressions, methods Reciever是T,可以被T和*T Type调用; methods Reciever是*T,则只能被*T Type调用;调用第一个参数类型要和调用者一致:T对应T类型变量,*T对用*T类型变量  
// T.Method.(var T)  
// (*T).Method.(var * T)  
  
        fmt.Println("------------------------")  
        m := Person.Getage(s1)  
        //m5 := Person.Getage(&s1) 报错,前后类型不一致  
        //m6 := Person.Getage(s2) 报错,前后类型不一致  
        m2 := (*Person).Getage(&s1)  
        n := (*Person).Getage(s2)  
        n2 := Person.Getage(*s2)  
        fmt.Println("m=",m)  
        fmt.Println("m2=",m2)  
        fmt.Println("n2=",n)  
        fmt.Println("n2=",n2)  
  
        fmt.Println("------------------------")  
        //Person.Setage(s1,9999)不允许, *T类型方法,T不能调用  
        //Person.Setage(&s1,9999)也是不允许,*T类型方法,T不能调用  
        (*Person).Setage(s2,500)  
        fmt.Println("s2=",s2)  
  
        //(*Person).Setage(s1,1000) 不允许 ,前后类型不一致  
        //Person.Setage(s2,1000) 不允许  
        (*Person).Setage(&s1,1000)  
        fmt.Println("s1=",s1)  
 }

其实有过面向对象经验,对于method value会比较容易理解,可以非常灵活使用。而对于method expression,重点理解如下一段话就可以:

methods Receiver
T,可以被
T
*T Type调用;

methods Receiver
*T,则只能被
*T Type调用;

调用的时候第一个参数类型要和调用者一致:T对应T类型变量,*T对用*T类型变量

参考:http://blog.csdn.net/hittata/…

    原文作者:soledad
    原文地址: https://segmentfault.com/a/1190000013139892
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞