在使用goLang
时,常常遇到Method Value
和Method Expressions
的问题,简单记录一下二者的使用区别
goLang
的type
类型方法定义以下:express
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
更直观,更底层,编译器不会进行优化,会按照实际表达意义去执行。this
将Method Value
赋值给变量,Receiver
实例当即被复制,这个和goLang
的一向做风是同样的。只进行值传递,即便是传递指针,也只是传递指针的拷贝,只是拷贝的指针和原指针都指向一个地方,看起来好像是直接操做原数据.net
instance.method(args)
:能够用 value(值)
或 pointer(指针)
变量调用全部绑定的任何方法,编译器会自动进行类型转换
,转换后的效果和method
的定义语义一致,和调用者的样式没有关系。指针
原method
定义的Receiver
是value
形的,即便使用pointer
实例变量调用该方法,也是value
值拷贝;原
method
定义的Receiver
是pointer
形的,即便使用value
实例变量调用该方法,也能改变Receiver
实例状态。code
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
类型变量blog