解释方法表达式的打印值

Explain the printed values of method expressions

以下代码尝试显示与结构关联的方法的地址。

package  main

import (
    "fmt"
    "reflect"
)

type II interface {
    Callme()
}

type Str struct {
    I int
    S string
}

func (s *Str) Callme () {
    fmt.Println("it is me")
}

func main() {
    s0 := &Str{}
    t := reflect.TypeOf(s0)
    v := reflect.ValueOf(s0)
    fmt.Println("Callme ", s0.Callme)   //real address ?
    fmt.Println(t.Method(0).Name, v.Method(0))    //real address ?

    s1 := &Str{}
    t1 := reflect.TypeOf(s1)
    v1 := reflect.ValueOf(s1)
    fmt.Println("Callme ", s1.Callme)   //real address ?
    fmt.Println(t1.Method(0).Name, v1.Method(0))    //real address ?
}

输出为:

Callme  0x4bc2d0
Callme 0x4ab2c0
Callme  0x4bc2d0
Callme 0x4ab2c0

所以我有两个问题:

fmt 包调用 Value.Pointer 获取函数地址。

让我们看一个例子,Value.Pointer returns 的功能:

s0 := &Str{}
v0 := reflect.ValueOf(s0)
fmt.Printf("s0.Callme: %0x %0x\n", reflect.ValueOf(s0.Callme).Pointer(), s0.Callme)
fmt.Printf("v0.Method(0) %0x %0x\n", v0.Method(0).Pointer(), v0.Method(0))

s1 := &Str{}
v1 := reflect.ValueOf(s1)
fmt.Printf("s1.Callme %x %x\n", reflect.ValueOf(s1.Callme).Pointer(), s1.Callme)
fmt.Printf("v1.Method(0) %x %x\n", v1.Method(0).Pointer(), v1.Method(0))

输出为:

s0.Callme: 105240 105240
v0.Method(0) eee60 eee60
s1.Callme 105240 105240
v1.Method(0) eee60 eee60

这与问题中显示的模式相符。

function related code for Value.Pointer是:

    if v.flag&flagMethod != 0 {
        // As the doc comment says, the returned pointer is an
        // underlying code pointer but not necessarily enough to
        // identify a single function uniquely. All method expressions
        // created via reflect have the same underlying code pointer,
        // so their Pointers are equal. The function used here must
        // match the one used in makeMethodValue.
        f := methodValueCall
        return **(**uintptr)(unsafe.Pointer(&f))
    }
    p := v.pointer()
    // Non-nil func value points at data block.
    // First word of data block is actual code.
    if p != nil {
        p = *(*unsafe.Pointer)(p)
    }
    return uintptr(p)

通过反射 API 中的 method expression 创建的 reflect.Value 设置了 flagMethod 方法位。正如注释状态和代码所示,Pointer 方法 returns 以这种方式创建的所有方法表达式的值相同。

relect.ValueOf(s1.Callme) 创建的 reflect.Value 没有设置 flagMethod 方法位。在这种情况下,函数 returns 指向实际代码的指针。

this program 的输出显示了所有组合:

type StrA struct {
    I int
    S string
}

func (s *StrA) Callme() {
    fmt.Println("it is me")
}

type StrB struct {
    I int
    S string
}

func (s *StrB) Callme() {
    fmt.Println("it is me")
}

s0A := &StrA{}
v0A := reflect.ValueOf(s0A)
s1A := &StrA{}
v1A := reflect.ValueOf(s0A)

fmt.Println("s0A.Callme ", reflect.ValueOf(s0A.Callme).Pointer())
fmt.Println("v0A.Method(0) ", v0A.Method(0).Pointer())
fmt.Println("s1A.Callme ", reflect.ValueOf(s1A.Callme).Pointer())
fmt.Println("v1A.Method(0) ", v1A.Method(0).Pointer())

s0B := &StrB{}
v0B := reflect.ValueOf(s0B)
s1B := &StrB{}
v1B := reflect.ValueOf(s0B)

fmt.Println("s0B.Callme ", reflect.ValueOf(s0B.Callme).Pointer())
fmt.Println("v0B.Method(0) ", v0B.Method(0).Pointer())
fmt.Println("s1B.Callme ", reflect.ValueOf(s1B.Callme).Pointer())
fmt.Println("v1B.Method(0) ", v1B.Method(0).Pointer())

输出:

s0A.Callme  1061824
v0A.Method(0)  978528
s1A.Callme  1061824
v1A.Method(0)  978528
s0B.Callme  1061952
v0B.Method(0)  978528
s1B.Callme  1061952
v1B.Method(0)  978528

我们可以观察到 Value.Pointer returns 通过反射 API 创建的所有 method expressions 的值相同。这包括不同类型的方法。

我们还可以观察到 Value.Pointer returns 对于给定类型和方法的所有 method expressions 都是相同的值。这适用于绑定到不同值的方法表达式。

Value.Pointer documentation 说:

If v's Kind is Func, the returned pointer is an underlying code pointer, but not necessarily enough to identify a single function uniquely. The only guarantee is that the result is zero if and only if v is a nil func Value.

鉴于此,应用程序无法可靠地使用 Value.Pointer 或通过 fmt 包打印的值来比较函数和方法。