[Go] 通过反射判断类型实现的接口方法是值接收器还是指针接收器

桃子 2019年9月21日

最近有一个需求需要检验 Go 语言的接口方式是值接收器(Value Receiver)方法还是指针接收器(Pointer Receiver)方法。貌似 Go 没有直接提供的方式,所以需要手动实现。 Go 语言里面,值类型T和对应的指针类型*T有着不同的方法集。T是所有值接收器方法的集合,而*T是所有值接收器方法和所有指针接收器方法的集合。

获取类型的两种方法集

如果有以下的类型:

type S struct{}

func (s S) A()  {}
func (s *S) B() {}

则可以通过如下的反射代码来分别获取T*T类型的方法集:

func main() {
	t := reflect.TypeOf(S{})
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Println("Value method:", m.Name)
	}

	fmt.Println()

	t = reflect.PtrTo(t)
	for i := 0; i < t.NumMethod(); i++ {
		m := t.Method(i)
		fmt.Println("Pointer method:", m.Name)
	}
}

以上代码会输出:

$ go run s.go
Value method: A

Pointer method: A
Pointer method: B

可以看到,值类型T只有值接收器方法,而指针类型*T同时有值接收器方法和指针接收器方法

所以总结一下:

  • 如果一个方法仅出现在指针类型的方法集中,那么它是指针接收器
  • 如果一个方法同时出现在指针类型值类型的方法集中,那么它是值接收器

通过反射判断方法是值接收器还是指针接收器

首先定义两个接口:

type IA interface {
	A()
}

type IB interface {
	B()
}

结构体还是上面代码的S结构体。

可以通过编写如下代码在运行时判断接收器的类型:

func main() {
	// 拿到两种接口的反射类型信息
	tia := reflect.TypeOf((*IA)(nil)).Elem()
	tib := reflect.TypeOf((*IB)(nil)).Elem()

	t := reflect.TypeOf(S{})

	// 如果指针类型实现了...
	if reflect.PtrTo(t).Implements(tia) {
		// 并且值类型也实现了 ...
		if t.Implements(tia) {
			fmt.Println("A is value receiver")
		} else {
			fmt.Println("A is pointer receiver")
		}

	}

	// 如果指针类型实现了...
	if reflect.PtrTo(t).Implements(tib) {
		// 并且值类型也实现了 ...
		if t.Implements(tib) {
			fmt.Println("B is value receiver")
		} else {
			fmt.Println("B is pointer receiver")
		}
	}
}

上面的代码将输出:

$ go run s.go
A is value receiver
B is pointer receiver

参考

发表于:2019年9月21日 ,阅读量:2625 ,标签:Go · 反射

如果文章有帮助到你,请我喝杯冰可乐吧~