Объясните напечатанные значения выражений метода - PullRequest
0 голосов
/ 22 января 2019

Следующий код пытается показать адрес метода, связанного со структурой.

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 Ответ

0 голосов
/ 22 января 2019

Пакет 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)

A 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 является значением nil func.

При этом приложение не может надежно использовать Value.Pointer или печатать значения через пакет fmt длясравнить функции и методы.

...