Перейти Отразить имя поля для конкретного интерфейса - PullRequest
0 голосов
/ 15 января 2019

У меня есть тип структуры с несколькими полями, которые все реализуют интерфейс Renderer. Типы полей реализуют интерфейс с указателями получателей. Я хотел бы иметь функцию, которая принимает имя поля и вызывает метод Render для этого поля. Я могу найти поле и получить много информации о нем, но выполнение утверждения типа, кажется, кусает меня из-за получателей указателя. Вот код, который показывает мою проблему:

package main

import (
    "fmt"
    "reflect"
)

type Renderer interface {
    Render()
}

type First struct {
    ExampleField Field
}

type Field []int

func (f *Field) Render() {
    fmt.Println("Hello from first")
}

func main() {
    f := First{
        Field{1, 2, 3},
    }
    f.ExampleField.Render()
    renderField("ExampleField", &f)
    renderField2("ExampleField", &f)
}

func renderField(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    renderType := reflect.TypeOf((*Renderer)(nil)).Elem()
    fieldToRender := structVal.FieldByName(field)
    fieldPtr := reflect.PtrTo(fieldToRender.Type())
    fmt.Printf("Implements? %v\n", fieldPtr.Implements(renderType))
    fmt.Printf("Addressable? %v\n", fieldToRender.CanAddr())
    fieldInter := fieldToRender.Interface()
    if renderer, ok := fieldInter.(Renderer); ok {
        // Pointer receiver so this never gets called
        fmt.Print("Able to cast")
        renderer.Render()
    }
}

func renderField2(field string, f *First) {
    structVal := reflect.ValueOf(*f)
    fieldToRender := structVal.FieldByName(field)
    vp := reflect.New(reflect.TypeOf(fieldToRender))
    vp.Elem().Set(reflect.ValueOf(fieldToRender))
    vpAddr := vp.Elem().Addr()
    typeVal := vpAddr.Interface()
    fmt.Println(typeVal) // <main.Field Value>⏎
    renderer := typeVal.(Renderer)
    renderer.Render()
    // interface conversion: *reflect.Value is not main.Renderer: missing method Render
}

renderField2, кажется, приближает меня, но Addr () дает мне * Reflect.Value, и когда я вызываю Interface (), вместо этого представляется базовый тип. Если я переключаюсь на приемник без указателя, то первая функция работает. Я обнаружил, что отражает значение Интерфейс и указатель приемника , который, кажется, почти точно соответствует тому, что я спрашиваю, и на вопрос дан ответ, но если я на самом деле вызываю метод isZeroer, представленный в ссылке для игровой площадки, он всегда ложен, поэтому он не кажется, на самом деле не отвечает на вопрос.

Кажется, что Addr является ключом, потому что в нем конкретно упоминаются приемники указателей, но я изо всех сил пытаюсь вернуть его обратно в интерфейс.

1 Ответ

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

Используйте этот код:

func renderField(name string, f *First) {
    structVal := reflect.ValueOf(f).Elem()
    field := structVal.FieldByName(name).Addr().Interface()
    if renderer, ok := field.(Renderer); ok {
        renderer.Render()
    }
}

Ключевым моментом является изменение:

structVal := reflect.ValueOf(*f)

до:

structVal := reflect.ValueOf(f).Elem()

Оператор, использованный в вопросе, создает неадресуемое значение структуры. Поля в неадресуемой структуре также не адресуемы, поэтому невозможно получить доступ к получателю указателя на полях.

Инструкция, используемая в этом ответе, создает адресуемое структурное значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...