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

最近有一个需求需要检验 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年09月21日 ,阅读量:80 ,标签:Go · 反射

版权声明:若非特别注明,本站所有文章均为作者原创,转载请务必注明原文地址。