在使用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