go 语言type Method Values vs. Method Expressions


go 语言的type 类型方法定义如下:

func (p mytype) funcname(q type) (r,s type) { return 0,0}

本质上这就是一种语法糖,方法调用如下:
instance.method(args) -> (type).func(instance, args)

instance 就是Reciever.左边的称为 Method Value,右边则是 Method Expression,go推荐使用左边形式。Method Value 是包装后的状态对象,总是与特定的对象实例关联在一起 (类似闭包,拐带私奔),而Method Expression 函数将 Receiver 作为第一个显式参数,调用时需额外传递。二者本质上没有区别,只是Method Value 看起来更像面向对象的格式,且编译器会自动进行类型转换;Method Expression更直观,更底层,编译器不会进行优化,会按照实际表达意义去执行,更易于理解。

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

instance.method(args):可以用 value 或 pointer 变量调用所有绑定的任何方法,编译器会自动进行类型转换,转换后的效果和methods的定义语义一致,和调用者的样式没有关系。比如原method定义的Receiver是value形的,即使使用pointer 实例变量调用该方法,也是value值拷贝;比如原method定义的Receiver是pointer形的,即使使用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)
 }




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值