Объясните напечатанные значения выражений метода
Следующий код пытается показать адрес метода, связанного со структурой.
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.Println("Callme ", s0.Callme) fmt.Println(t.Method(0).Name, v.Method(0))
Во-вторых, почему эти утверждения показывают одинаковые значения?
fmt.Println(t.Method(0).Name, v.Method(0)) fmt.Println(t1.Method(0).Name, v1.Method(0))
1 ответ
fmt
пакет вызывает Value.Pointer для получения адресов функций.
Давайте посмотрим на пример того, что Value.Pointer
возвращает для функций:
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
Это соответствует схеме, показанной в вопросе.
Код, связанный с функцией для 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)
reflect.Value
созданный с помощью выражения метода в отражающем API имеет flagMethod
бит метода установлен. Как отмечается в комментарии и показывается код, метод Pointer возвращает одинаковое значение для всех выражений метода, созданных таким образом.
reflect.Value
создано relect.ValueOf(s1.Callme)
не имеет flagMethod
бит метода установлен. В этом случае функция возвращает указатель на фактический код.
Выходные данные этой программы показывают все комбинации:
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 возвращает одно и то же значение для всех выражений методов, созданных через отражающий API. Это включает в себя методы разных типов.
Мы также можем наблюдать, что Value.Pointer
возвращает одно и то же значение для всех выражений метода для данного типа и метода. Это верно для выражений метода, связанных с различными значениями.
Документация Value.Pointer гласит:
Если v для Kind - Func, возвращаемый указатель является указателем на базовый код, но этого не обязательно достаточно для уникальной идентификации одной функции. Единственная гарантия состоит в том, что результат равен нулю тогда и только тогда, когда v является нулевым значением Func.
Учитывая это, приложение не может надежно использовать Value.Pointer или печатные значения через fmt
пакет для сравнения функций и методов.