golang中,struct的method的形式如下:
func (r ReceiverType) funcName(parameters) (results)
如果想要修改struct的成员的值,method被定义时候其ReceiverType必须是struct*形式。如果ReceiverType是struct,则无法改变struct成员的值。
废话少说,代码验证:
package main
import (
"fmt"
)
type tag struct {
value int32
}
func (t tag) Change() {
t.value = int32(987)
}
// The method set of any other named type T consists of all methods with receiver type T.
// The method set of the corresponding pointer type *T is the set of all methods with
// receiver *T or T (that is, it also contains the method set of T).
// 定义receiver的method的时候,一般receiver使用指针形式,因为T也可以调用T*的函数,并能否修改器成员
// 编译器会参考上面的函数隐式的生成下面的函数
// func (t *tag) Change() {
// t.value = int32(987)
// }
type tag2 struct {
value int32
}
func (t *tag2) Change() {
t.value = int32(987)
}
func main() {
// tag*
tp := new(tag)
tp.value = 123
tp.Change()
fmt.Println(tp) // &{123}
// tag
var tv = *tp
tv.Change()
fmt.Println(tv) // {123}
// tag2
tv2 := tag2{41}
tv2.Change()
fmt.Println(tv2) // {987}
// tag2*
var tp2 = &tag2{value: 123}
tp2.Change()
fmt.Println(tp2) // &{987}
}
上面main函数中,第一段代码中对象 tp 的形式为*tag,但是其方法Change无法改变其value值。第二段代码中对象 tv2 的形式为tag2,但是其方法Change却可以改变其value值。
如果有人感兴趣,我就接着给说道说道。
golang中的method的第一个参数就是它的Receiver Type,可以是值形式也可以是指针形式,而c++以及其同类语言java等C系语言中method的方法默认是class* this。也就是说,golang中method有传对象值与传对象地址的值两种,而C系语言强制要求传递对象的地址。最终改变对象值与否取决于Receiver Type,而不取决于 object Variable Type。
这么说,就可以理解了吧?Golang中如果函数需要改变对象成员的值,需要Receiver Type为指针形式,若非必要则不要使用指针形式,因为指针形式的对象会被分配在内存堆上,耗费GC资源。
再补充两个代码示例:
example1:
/******************************************************
# DESC :
# AUTHOR : Alex Stocks
# MOD : 2016-09-05 07:26
# FILE : value_ptr_receiver.go
******************************************************/
package main
type tag struct {
}
func (this *tag) String() {
println("hello")
}
type Tag struct {
}
func (this Tag) String() {
println("hello")
}
func main() {
var t tag
t.String()
var tt *Tag = &Tag{}
(*tt).String()
}
example2:
type T struct {
A int // A is name, int is type, T.A is interface
B string
}
/*
从test例子可以看出,func (this T) GetTName(str string) string有一个隐式的函数:
func (this *T) GetTName(str string) string
*/
func (this T) GetTName(str string) string {
this.B = str
return this.B
}
func (this *T) GetName(str string) string {
this.B = str
return this.B
}
func test_field_method() {
t := T{203, "mh203"}
// s := reflect.ValueOf(&t).Elem()
// s := reflect.ValueOf(t)
s := reflect.Indirect(reflect.ValueOf(t)) // 上面三行等价
typeOfT := s.Type()
/*
0: A int = 203
1: B string = mh203
*/
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}
/*
idx: 0
func(main.T, string) string
GetTName
NumIn: 2
in(0) type: main.T
in(1) type: string
NumOut: 1
out(1) type: string
*/
for m := 0; m < reflect.TypeOf(t).NumMethod(); m++ {
fmt.Println("idx:", m)
method := reflect.TypeOf(t).Method(m)
fmt.Println(method.Type) // func(*main.MyStruct) string
fmt.Println(method.Name) // GetName
fmt.Println("NumIn:", method.Type.NumIn()) // 参数个数
fmt.Println("in(0) type:", method.Type.In(0)) // 参数类型
fmt.Println("in(1) type:", method.Type.In(1)) // 参数类型
fmt.Println("NumOut:", method.Type.NumOut()) // 返回值个数
fmt.Println("out(1) type:", method.Type.Out(0)) // 第一个返回值类型
}
/*
idx: 0
func(*main.T, string) string
GetName
NumIn: 2
in(0) type: *main.T
in(1) type: string
NumOut: 1
out(1) type: string
idx: 1
func(*main.T, string) string
GetTName
NumIn: 2
in(0) type: *main.T
in(1) type: string
NumOut: 1
out(1) type: string
*/
for m := 0; m < reflect.TypeOf(&t).NumMethod(); m++ {
fmt.Println("idx:", m)
method := reflect.TypeOf(&t).Method(m)
fmt.Println(method.Type) // func(*main.MyStruct) string
fmt.Println(method.Name) // GetName
fmt.Println("NumIn:", method.Type.NumIn()) // 参数个数
fmt.Println("in(0) type:", method.Type.In(0)) // 参数类型
fmt.Println("in(1) type:", method.Type.In(1)) // 参数类型
fmt.Println("NumOut:", method.Type.NumOut()) // 返回值个数
fmt.Println("out(1) type:", method.Type.Out(0)) // 第一个返回值类型
}
}